/**
 * Project Name:myutils
 * Date:2018年12月16日
 * Copyright (c) 2018, jingma All Rights Reserved.
 */

package cn.benma666.sjsj.web;

import cn.benma666.constants.SysAuth;
import cn.benma666.crypt.MD5Util;
import cn.benma666.dict.*;
import cn.benma666.domain.*;
import cn.benma666.excel.ImageConverter;
import cn.benma666.excel.ImageConverterKey;
import cn.benma666.excel.MyLongestMatchColumnWidthStyleStrategy;
import cn.benma666.exception.DictException;
import cn.benma666.exception.MyException;
import cn.benma666.exception.QxException;
import cn.benma666.exception.VerifyRuleException;
import cn.benma666.json.JsonUtil;
import cn.benma666.iframe.*;
import cn.benma666.myutils.DateUtil;
import cn.benma666.myutils.*;
import cn.benma666.params.other.Dcsjycl;
import cn.benma666.sjsj.myutils.Msg;
import cn.benma666.sjsj.myutils.MyTransactional;
import cn.benma666.sjsj.myutils.ThreadPool;
import cn.benma666.sjsj.znjh.MyZnjh;
import cn.benma666.sjzt.*;
import cn.benma666.sjzt.bdwj.BdwjFile;
import cn.benma666.vo.WebSocketKhdxxVo;
import com.alibaba.druid.util.Utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.util.TypeUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.beetl.sql.clazz.SQLType;
import org.beetl.sql.clazz.kit.StringKit;
import org.beetl.sql.core.*;
import org.beetl.sql.core.mapping.BeanProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 默认拦截器 <br/>
 * date: 2018年12月16日 <br/>
 *
 * @author jingma
 */
@Component("MyDefaultLjq")
@Scope("prototype")
public class DefaultLjq extends BasicObject implements LjqInterface {
    /**
     * 去重字段列表缓存后缀
     */
    public static final String QCZD_LIST = "_qczdList";
    /**
     * 数据对象Map<对象key，对象>
     */
    protected static final JSONObject sjdxMap = CacheFactory.use(KEY_SJDX);
    /**
     * 字段信息缓存
     */
    protected static JSONObject fieldsCache = CacheFactory.use(KEY_FIELDS, CacheFactory.TYPE_MEMORY);
    /**
     * 允许的文件类型
     */
    protected static Collection<String> yxdwjlx = Arrays.asList(
            valByDef(Conf.getUtilConfig().getService().getYxscwjlx(),
            "xls,xlsx,txt,csv,zip,rar,7z,png,jpg,json,xml").split(","));

    public DefaultLjq(){
    }
    /**
     * 获取处理类型
     *
     * @param myParams 参数对象
     * @return 处理类型
     */
    public static String getCllx(MyParams myParams) {
        return myParams.sys().getCllx();
    }

    public Result initSjdx(MyParams myParams) throws MyException{
        if (StringUtil.isBlank(getCllx(myParams))) {
            throw new MyException(Msg.msg("interceptor.bxsscllx", "sys.cllx"));
        }
        //记录请求开始时间
        myParams.sys().setQqkssj(System.currentTimeMillis());
        //获取用户信息
        @Nullable SysQxYhxx user = LjqManager.getUser(myParams);
        myParams.put(KEY_USER, user);
        //穿透场景不进行对象处理
        if(isCt(myParams)){
            //记录操作日志
            initCzrz(myParams, user);
            return success("获取基础信息成功",myParams);
        }
        SysSjglSjdx sjdx;
        //读取缓存
        String cacheKey = StringUtil.fastNotNull(myParams.sys().getDlsjdxid(),
                myParams.sys().getDlsjdxdm(),myParams.sys().getAuthCode(),
                myParams.sjdx().getId(),myParams.sjdx().getDxdm());
        if(!isBlank(cacheKey)){
            synchronized (sjdxMap){
                //进行同步操作，不免重复查询，缓存加载完成后这里应该耗时很少，应该不会成为瓶颈
                Object obj = sjdxMap.get(cacheKey);
                if (obj != null && !valByDef(myParams.sys().getClearCache(),false)) {
                    sjdx = (SysSjglSjdx) obj;
                } else {
                    JSONObject jsonObj = Db.use().findFirst(SqlId.of("sjsj", "findSjdx"), myParams);
                    if (jsonObj == null) {
                        throw new MyException(Msg.msg("interceptor.sjdxbwy", cacheKey), myParams);
                    }
                    sjdx = jsonObj.toJavaObject(SysSjglSjdx.class);
                    //设置缓存
                    sjdxMap.put(cacheKey,sjdx);
                }
            }
        }else {
            throw new MyException("该请求没有设置数据对象信息");
        }
        JSONObject defParams = JSONObject.parseObject(Conf.getVal("sjdx.jcxx1"), Feature.OrderedField);
        //合并新配置与默认配置
        myParams.putAll(JsonUtil.mergeJSONObjects(defParams,myParams));
        if(!myParams.sys().getNbdy()&&!UserManager.hasAuth(user, SysAuth.QTQX_SYS.name())){
            //非内部调用且没有最高权限
            defParams= JSONObject.parseObject(Conf.getVal("sjdx.jcxx2"), Feature.OrderedField);
            //合并优先级高于用户传参的默认配置2（核心关键配置），对前端传参进行控制，避免前端传参权力太大，入侵系统
            JsonUtil.mergeJSONObjects(myParams,defParams);
        }
        //合并数据对象的扩展信息到系统参数中
        if(sjdx!=null){
            if(valByDef(Conf.getUtilConfig().getZnjh().getZdctdy(), true)){
                log.trace("开始进行自动补充穿透调用:{},{},{}",sjdx,sjdx.getSjztObj(),myParams.sys().getCtapp());
                if(!isBlank(sjdx.getSjztObj().getSsyy())&&isBlank(myParams.sys().getCtapp())){
                    //数据载体设置了所属应用且外部没有设置穿透app时自动补充
                    myParams.sys().setCtapp(sjdx.getSjztObj().getSsyy());
                }
                if(!isBlank(myParams.sys().getCtapp())){
                    //自动设置穿透调用，有穿透app时，查看是否需要进行穿透调用
                    SysZnjhCtdy ctdy = MyZnjh.getCtdyByMbjd(myParams.sys().getCtapp());
                    if (!isBlank(ctdy.getDm())) {
                        myParams.sys().setCtdy(ctdy.getDm());
                    }
                }
            }
            //设置从数据库中读取的数据对象
            myParams.put(KEY_SJDX, sjdx);
            //通过大字典扩展对象，对象本地的扩展信息字段大小限制4000可能存不下，较少使用
            String kzxx = Conf.getVal(sjdx.getDxdm() + ".kzxx");
            if(!isBlank(kzxx)){
                JsonUtil.mergeJSONObjects(myParams, JSONObject.parseObject(kzxx, Feature.OrderedField));
            }
            JsonUtil.mergeJSONObjects(myParams, sjdx.getKzxxObj());
            //对rest接口的支持（支持路径参数传递一个关键参数）：SEARCHKEY处理，非查询场景，设置了该参数时,且原始参数没有设置主键，自动设置为对象主键
            if(!Cllx.select.name().equals(myParams.sys().getCllx())
                    &&StringUtil.isNotBlank(myParams.sys().getSearchKey())
                    &&isBlank(myParams.get("$.yobj."+sjdx.getZddmZjzd()))){
                myParams.set("$.yobj."+sjdx.getZddmZjzd(),myParams.sys().getSearchKey());
            }
        }
        //穿透场景不获字段等信息
        if(isCt(myParams)){
            //记录操作日志
            initCzrz(myParams, user);
            return success("获取基础信息成功",myParams);
        }
        //获取基础信息
        return success("获取基础信息成功",LjqManager.jcxx(myParams));
    }

    /**
     * 是否穿透
     */
    public boolean isCt(MyParams myParams){
        //穿透场景不获字段等信息
        if(!isBlank(myParams.sys().getCtqapp())&&!Conf.getAppdm()
                .equals(myParams.sys().getCtqapp())){
            //穿透前app调用
            return true;
        }else if(!isBlank(myParams.sys().getCtdy())){
            return true;
        }else if(!isBlank(myParams.sys().getCtapp())&&!Conf.getAppdm()
                .equals(myParams.sys().getCtapp())){
            return true;
        }
        return false;
    }
    public Result jcxx(MyParams myParams) {
        SysQxYhxx user = myParams.user();
        //处理权限码
        if(isBlank(myParams.sys().getAuthCode())){
            //若前端没有传入权限码
            if(user!=null&&getSjdx()!=null&&user.getDxqxys().get(getSjdx().getDxdm())!=null){
                //优先取当前用户获得授权的权限码
                myParams.sys().setAuthCode(user.getDxqxys().get(getSjdx().getDxdm()).getString("qxm"));
            }else if(getSjdx()!=null){
                //若都没有授权则随机取，其实取哪个已经没意义了，反正都一样没权限
                myParams.sys().setAuthCode(getSjdx().getAuthCode());
            }else if(isBlank(myParams.sys().getController())&&user!=null
                &&user.getDxqxys().get(myParams.sys().getController())!=null){
                //定制场景
                myParams.sys().setAuthCode(user.getDxqxys().get(myParams.sys()
                        .getController()).getString("qxm"));
            }
        }
        //获取字段信息
        if(getSjdx()!=null){
            //没有数据对象不获取字段
            try{
                getFields(myParams, user);
            }catch (MyException e){
                //权限鉴定
                auth(myParams);
                throw e;
            }
        }
        //记录操作日志
        initCzrz(myParams, user);
        //权限鉴定
        auth(myParams);
        //非定制场景
        if(getSjdx()!=null){
            if (myParams.getBooleanValue("$.cllxkz."+getCllx(myParams)+".putobj")) {
                //设置需要处理的对象
                putObj(myParams,false);
            }
            //转换规则
            zhgz(myParams);
            //参数预处理
            csycl(myParams);
            //验证规则
            yzgz(myParams);
        }
        log.trace("基础信息获取完成");
        return success("获取基础信息成功",myParams);
    }

    /**
     * 参数预处理
     */
    protected void csycl(MyParams myParams) {
        JSONObject yobj = myParams.getJSONObject(KEY_YOBJ);
        if(isBlank(yobj)){
            return;
        }
        String cllx = getCllx(myParams);
        Map<String, SysSjglSjzd> fields = (Map<String, SysSjglSjzd>) myParams.get(KEY_FIELDS);
        for(Map.Entry<String,SysSjglSjzd> e:fields.entrySet()){
            if (isBlank(yobj.get(e.getKey()))){
                //空值忽略
                continue;
            }
            SysSjglSjzd f = e.getValue();
            JSONArray list;
            if(f.getZdywlbNew().contains("plcz")){
                //混合保存支持
                list = yobj.getJSONArray(e.getKey());
                //参数重组
                yobj.remove(e.getKey());
                if("MyFile".equals(f.getKjlx())){
                    //文件控件，表示通过文件批量上传的数据
                    myParams.sys().setFiles(list);
                    //转为批量保存
                    if(Cllx.insert.name().equals(cllx)||Cllx.update.name().equals(cllx)
                            ||Cllx.save.name().equals(cllx)){
                        //一般保存转为数据批量上传
                        myParams.sys().setCllx(Cllx.sjplsc.name());
                    }
                }else {
                    List<JSONObject> nList = new ArrayList<>();
                    JSONObject no;
                    for (JSONObject o:list.toJavaList(JSONObject.class)){
                        no = yobj.clone();
                        JsonUtil.mergeJSONObject(no,o);
                        nList.add(no);
                    }
                    myParams.sys().setEditTableData(nList);
                    myParams.remove(KEY_YOBJ);
                    //转为批量保存
                    if(Cllx.insert.name().equals(cllx)||Cllx.update.name().equals(cllx)
                            ||Cllx.save.name().equals(cllx)){
                        //一般保存转为批量保存
                        myParams.sys().setCllx(Cllx.plbc.name());
                    }
                }
            }else if(f.getZdywlbNew().contains("glcz")){
                //关联操作
                Object val = yobj.get(e.getKey());
                yobj.remove(e.getKey());
                MyParams glcs = new MyParams(f.getKzxxObj().getJSONObject("glcs"));
                if(isBlank(glcs)){
                    throw new MyException("关联操作参数不能为空："+e.getKey());
                }
                if(val instanceof List){
                    if("MyFile".equals(f.getKjlx())){
                        //文件控件，表示通过文件批量上传的数据
                        glcs.sys().setFiles((List) val);
                        cllx = getCllx(glcs);
                        //转为批量保存
                        if(Cllx.insert.name().equals(cllx)||Cllx.update.name().equals(cllx)
                                ||Cllx.save.name().equals(cllx)){
                            //一般保存转为数据批量上传
                            glcs.sys().setCllx(Cllx.sjplsc.name());
                        }
                    }else {
                        glcs.sys().setEditTableData((List) val);
                    }
                }else {
                    glcs.put(KEY_YOBJ,val);
                }
                myParams.sys().getCstcl().put(f.getZdmc(),glcs);
            }
        }
    }

    /**
     * 转换规则
     * @param myParams 相关参数
     */
    public void zhgz(MyParams myParams) {
        JSONObject zhgz = myParams.getJSONObject(KEY_ZHGZ);
        if (zhgz==null) {
            //没有配置转换信息
            return;
        }
        //取出验证规则
        String cllx = getCllx(myParams);
        for (String key : zhgz.keySet()) {
            JSONObject gzObj = zhgz.getJSONObject(key);
            Object val = myParams.get("$." + key);
            Object newVal = TransRule.ruleTrans(val,myParams, gzObj, cllx);
            if((isBlank(val)&&isBlank(newVal))){
                //原始值为空，转换后也为空则跳过
                continue;
            }
            //转换数据
            myParams.set("$." + key,newVal);
        }
    }

    /**
     * 验证规则
     * @param myParams 相关参数
     */
    public void yzgz(MyParams myParams) {
        //取出验证规则
        JSONObject yzgz = myParams.getJSONObject(KEY_YZGZ);
        if (myParams.get(KEY_USER) == null||yzgz==null) {
            //没有用户信息默认为系统内部调用，不进行验证，或没有配置验证信息
            return;
        }
        String cllx = getCllx(myParams);
        for (String key : yzgz.keySet()) {
            JSONObject gzObj = yzgz.getJSONObject(key);
            try {
                Object val = myParams.get("$." + key);
                Object newVal = VerifyRule.ruleVerify(myParams.get("$." + key),
                        myParams, gzObj, cllx);
                if((isBlank(val)&&isBlank(newVal))){
                    //原始值为空，转换后也为空则跳过
                    continue;
                }
                //转换数据
                myParams.set("$." + key,newVal);
            } catch (VerifyRuleException e) {
                throw new MyException(e.getMessage(), HttpStatus.PRECONDITION_FAILED.value(), key);
            }
        }
    }

    /**
     * 采用base64的方式上传
     */
    public Result upload(MyParams myParams) throws Exception {
        SysSjglFile file = myParams.yobj(SysSjglFile.class);
        if(StringUtil.isBlank(file.getWjnr())){
            throw new MyException("没有获取到上传文件内容");
        }
        if(!yxdwjlx.contains(file.getWjlx())){
            throw new MyException("上传的文件类型不支持："+file.getWjlx());
        }
        if(StringUtil.isEmpty(file.getYwdm())){
            file.setYwdm("dxsjsc");
        }
        if(StringUtil.isEmpty(file.getWjlb())){
            file.setWjlb(getSjdx().getDxdm());
        }
        byte[] byteArr = Base64.getDecoder().decode(file.getWjnr());
        //去重码ywdm+wjlb+MD5
        file.setQcm(file.getYwdm() + file.getWjlb() + FileUtil.getFileMD5(byteArr));
        //如果表中存在此去重码则把这个文件删除
        MyLambdaQuery<SysSjglFile> fileMyLambdaQuery = db(DEFAULT).lambdaQuery(SysSjglFile.class,myParams.user());
        SysSjglFile f = fileMyLambdaQuery.andEq(SysSjglFile::getQcm,file.getQcm()).singleSimple();
        if (f!=null) {
            log.info(f.getWjm() + "文件已经存在"+f.getId());
            return success("该文件已经存在", f.toQd());
        }
        IFile ifile = file.toIFile();
        ifile.save(new ByteArrayInputStream(byteArr));
        //保存文件记录
        file.setWjdx(byteArr.length);
        file.setSclj(ifile.getAbsolutePath());
        file.setParent(ifile.getParent());
        fileMyLambdaQuery.insertSelective(file);
        //避免文件内容打印到日志和返回前端
        file.setWjnr(null);
        log.debug(file.getWjm() + "文件上传成功");
        return success("文件上传成功", file.toQd());
    }

    /**
     * 只取对外有用的,系统基础信息
     * @return 系统基础信息
     */
    public Result xtjcxx(MyParams myParams){
        MyParams jcxx = new MyParams();
        jcxx.put(KEY_USER,myParams.user().toQd());
        JsonUtil.copy(jcxx, myParams, $_SYS_TOKEN);
        JsonUtil.copy(jcxx, myParams, $_SYS_CLIENT_IP);
        //设置在线用户数,可能会多计算一个
        jcxx.sys().setZxyhs(XtxxWebSocket.getOnLineHhs()+1);
        return success("基础信息获取成功", jcxx);
    }
    /**
     * 只取对外有用的,对象基础信息
     * @return 对象基础信息
     */
    public Result dxjcxx(MyParams myParams){
        //只取对外有用的，对象基础信息
        JSONObject jcxx = new JSONObject();
        jcxx.put(KEY_SJDX, myParams.sjdx());
        jcxx.put(KEY_FIELDS, myParams.fields());
        jcxx.put(KEY_ZDDMS, myParams.fields().keySet());
        jcxx.put(KEY_OBJ, myParams.obj());
        JsonUtil.copy(jcxx, myParams, "$.cllxkz");
        JsonUtil.copy(jcxx, myParams, "$.sys.fields");
        JsonUtil.copy(jcxx, myParams, "$.sys.cdlj");
        JsonUtil.copy(jcxx, myParams, $_SYS_AUTHCODE);
        return success("对象基础信息获取成功", jcxx);
    }

    public Result getdata(MyParams myParams) {
        //创建个对象，用于在模板中可以设置一些数据作为结果传给前端
        JSONObject resultData = new JSONObject();
        myParams.sql().setResultData(resultData);

        //先调用一次查询语句，获取默认查询条件
        LjqManager.getSql(myParams,Cllx.select.name());
        String[] arr = (String[]) getSql(myParams).getData();
        resultData.put("list", db(arr[0]).find(arr[1], myParams));
        return success("操作成功", resultData);
    }


    public Result plcl(MyParams myParams) {
        //先调用一次查询语句，获取默认查询条件
        LjqManager.getSql(myParams,Cllx.select.name());
        String[] arr = LjqManager.getSql(myParams);
        final int update = db(arr[0]).update(arr[1], myParams);
        return success("操作成功："+update, update);
    }

    /**
     * 导出当前页数据
     */
    public Result dcdqysj(MyParams myParams) {
        return dcsj(myParams);
    }

    public Result dcsj(MyParams myParams) {
        //导出数据
        myParams.set("$.page.totalRequired", Boolean.FALSE);
        myParams.set("$.page.listRequired", Boolean.TRUE);
        PageInfo page = (PageInfo) LjqManager.select(myParams).getData();
        String fileName = getSjdx().getDxmc()+"-" + DateUtil.getGabDate();
        try {
            dcsjYcl(myParams,page.getList(),Cllx.dcsj.name());
            return resultExcelFile(myParams, myParams.other().getDcsjycl().getHeader(),
                    myParams.other().getDcsjycl().getData(), fileName);
        } catch (Exception e) {
            throw new MyException("导出数据失败，请查看系统日志分析原因",e);
        }
    }


    public Result getfile(MyParams myParams) {
        String[] arr;
        try {
            arr = (String[]) LjqManager.getSql(myParams);
            List<JSONObject> rl = db(arr[0]).find(arr[1], myParams);
            if (rl.size() == 0) {
                throw new MyException("未找到文件数据");
            }
            JSONObject fo = rl.get(0);
            SysSjglFile file = fo.toJavaObject(SysSjglFile.class);
            String sjxs = fo.getString("sjxs");
            byte[] byteArr = null;
            if ("bdwjjl".equals(sjxs)) {
                byteArr = Utils.readByteArray(new FileInputStream(fo.getString("data")));
            } else if ("blob".equals(sjxs)) {
                byteArr = fo.getBytes("data");
            }
            if(myParams.containsKey("$.sys.xzms")){
                file.setXzms(myParams.getIntValue("$.sys.xzms"));
            }
            return resultFile(byteArr, file);
        } catch (Exception e) {
            log.error("获取文件失败:" + myParams, e);
            throw new MyException("获取文件失败，请查看系统日志分析原因:" + e.getMessage());
        }
    }

    /**
     * 下载文件
     * @param myParams 相关参数
     * @return 处理结果
     */
    public Result download(MyParams myParams) {
        MyParams fileJcxx = LjqManager.jcxxByDxdm("SYS_SJGL_FILE",myParams.user());
        fileJcxx.set("$.page.totalRequired", Boolean.FALSE);
        fileJcxx.put(KEY_YOBJ,myParams.get(KEY_YOBJ));
        fileJcxx.yobj(SysSjglFile.class).setYxx(Ljpd.TURE.getCode()+"");
        JsonUtil.copy(fileJcxx,myParams,$_SYS_IDS);
        PageInfo<JSONObject> page = (PageInfo<JSONObject>) LjqManager.select(fileJcxx).getData();
        if (page.getList().size() == 0) {
            throw new MyException("没有找到该文件");
        } else if (page.getList().size() > 1) {
            throw new MyException("不能找到唯一的文件记录");
        }
        SysSjglFile file = page.getList().get(0).toJavaObject(SysSjglFile.class);
        return resultFile(file.getFileBytes(), file);
    }



    public Result sjplsc(MyParams myParams) {
        JSONArray files = myParams.getJSONArray("$.sys.files");
        if(isBlank(files)){
            throw new MyException("没有获取到文件，请确认文件正确上传");
        }
        Result r = success("");
        int count = 0;
        int insert = 0;
        int update = 0;
        int ignore = 0;
        int repeat = 0;
        List<String> succFiles = new ArrayList<>();
        SysSjglFile file = null;
        try {
            for(int i=0;i<files.size(); i++){
                file = Db.use().lambdaQuery(SysSjglFile.class).andEq(SysSjglFile::getId,
                        files.getObject(i,SysSjglFile.class).getId()).singleSimple();
                //采用流式处理，避免全部读取到内存
                SjdxExcelReader er = new SjdxExcelReader(getSjdx(),myParams,file);
                //此处不验证数据，后续的批量保存会进行校验
                er.setSjyz(false);
                er.disposeExcel();
                myParams.set("$.sys.editTableData", er.getResult());
                r = LjqManager.data(myParams,Cllx.plbc.name());
                JSONObject data = r.getData(JSONObject.class);
                count += er.getResult().size();
                repeat += data.getIntValue("repeat");
                insert += data.getIntValue("insert");
                update += data.getIntValue("update");
                ignore += data.getIntValue("ignore");
                succFiles.add(file.getWjm()+"成功处理"+er.getResult().size()+"条记录");
            }
        } catch (MyException e) {
            r = e.getResult();
        } catch (Exception e) {
            r = new MyException("文件处理失败",e).getResult();
        }
        if(!r.isStatus()&&file!=null){
            r.addMsg("异常文件："+file.getWjm());
        }
        if(count>0){
            r.setMsg(StringUtil.join(succFiles,","));
            Result r1 = sjplcljg(count, insert, update, ignore, repeat);
            r.addMsg(r1.getMsg());
            r.setData(r1.getData());
        }
        return r;
    }

    /**
     * 批量数据转换验证
     * @param myParams 参数体
     * @return 验证转换结果
     */
    public Result plsjzhyz(MyParams myParams) {
        JSONArray editTableData = myParams.getJSONArray($_SYS_EDITTABLEDATA);
        if(isBlank(editTableData)){
            return success("没有提供批量转换验证的数据");
        }
        //重复数据量
        List<JSONObject> list = editTableData.toJavaList(JSONObject.class);
        //处理后的数据列表，用于后续批量插入更新
        List<JSONObject> dataList;
        if (Db.isSupported(getSjdx().getDxztlx()) || SjztLx.es.name().equals(getSjdx().getDxztlx())) {
            //数据库场景才进行查重操作
            dataList = new ArrayList<>();
            List<Object> qcList = new ArrayList<>();
            String qczd = null;
            //先查询重复数据
            for (JSONObject j : list){
                myParams.put(KEY_YOBJ, j);
                //根据输入参数进行重复查询
                JSONObject qcObj = putObj(myParams,true).getData(JSONObject.class);
                if (isBlank(qcObj)) {
                    //没有去重的直接加入待处理列表
                    dataList.add(j);
                }else {
                    //设置去重字段
                    qczd = qcObj.keySet().toArray()[0].toString();
                    //去重字段值
                    qcList.add(qcObj.values().toArray()[0]);
                }
            }
            //基于去重字段的数据MAP
            Map<String, JSONObject> qczdMap = isBlank(qczd)?new HashMap<>():Db.listToMap(list, qczd);
            if(!isBlank(qcList)){
                myParams.sql().setQczd(StringKit.enCodeUnderlined(qczd));
                //不进行总量查询
                myParams.page().setTotalRequired(Boolean.FALSE);
                myParams.page().setPageSize(list.size()*2);
                myParams.put(KEY_YOBJ, new JSONObject());
                JSONObject yobj;
                //按20000分批次查询，避免sql太长
                List<List<Object>> qcList1 = CollectionUtils.partition(qcList, 20000);
                Map<String ,SysSjglSjzd> qczdList = (Map<String ,SysSjglSjzd>) fieldsCache.get(getSjdx().getId()+ QCZD_LIST);
                for(List<Object> ql:qcList1){
                    //设置根据去重字段查询数据的条件，按1000分组，oracle对in的数量有限制
                    myParams.sql().setQcList(CollectionUtils.partition(ql, 1000));
                    List<JSONObject> sList = select(myParams).getPageList(JSONObject.class);
                    for(JSONObject o:sList){
                        //移除查询到的数据
                        yobj = qczdMap.get(o.getString(qczd));
                        if(yobj==null){
                            //查询到多条时会出现
                            continue;
                        }
                        qczdMap.remove(o.getString(qczd));
                        //把查询出来的主键设置到原输入参数中，便于配置了去重字段的场景基于主键进行更新
                        setYobjByObj(yobj, qczdList, o);
                        //设置已找到记录
                        yobj.put("my-yzdjl",true);
                        yobj.put("my-obj",o);
                        dataList.add(yobj);
                    }
                }
            }
            //将没查询到追加到待处理集合中
            dataList.addAll(qczdMap.values());
        }else {
            dataList = list;
        }
        List<SysLogSjsccw> errorList = new ArrayList<>();
        Map<String, SysSjglSjzd> fieldMap = (Map<String, SysSjglSjzd>) myParams
                .get(KEY_FIELDS);
        JSONObject zhgz = myParams.getJSONObject(KEY_ZHGZ);
        JSONObject yzgz = myParams.getJSONObject(KEY_YZGZ);
        Object val;
        String cllx;
        JSONObject ruleObj;
        Object newVal;
        boolean sjyz = !(myParams.sys().getNbdy()||(UserManager.hasAuth(myParams.user(),"QTQX_SJTB")
                &&!TypeUtils.castToBoolean(valByDef(myParams.sys().getSjyz(),1))));
        for (int i=0;i<dataList.size();i++){
            JSONObject yobj = dataList.get(i);
            myParams.put(KEY_YOBJ, yobj);
            myParams.put(KEY_OBJ, yobj.get("my-obj"));
            //根据库中记录是否找到设置验证处理类型
            cllx = yobj.getBooleanValue("my-yzdjl")?Cllx.update.name():Cllx.insert.name();
            for (Map.Entry<String, SysSjglSjzd> e : fieldMap.entrySet()) {
                val = yobj.get(e.getKey());
                //转换规则
                ruleObj = zhgz.getJSONObject("yobj."+ e.getKey());
                if(ruleObj!=null){
                    //转换数据
                    newVal = TransRule.ruleTrans(val,myParams, ruleObj, cllx);
                    if((!isBlank(val)||!isBlank(newVal))){
                        yobj.put(e.getKey(),newVal);
                    }
                }
                if(!sjyz){
                    //不验证跳过
                    continue;
                }
                ruleObj = yzgz.getJSONObject("yobj." + e.getKey());
                if(ruleObj!=null){
                    try {
                        newVal = VerifyRule.ruleVerify(val, myParams,ruleObj,cllx);
                        if((!isBlank(val)||!isBlank(newVal))){
                            yobj.put(e.getKey(), newVal);
                        }
                    } catch (VerifyRuleException e1) {
                        SysLogSjsccw sysLogSjsccw = new SysLogSjsccw(yobj.getString("mySswj"),val + "", e.getValue().getZdmc(),
                                (i+1) + "", getSjdx().getId(), (e.getValue().getPx() + 1) + "", e1.getMessage(),
                                myParams.sys().getQqid());
                        errorList.add(sysLogSjsccw);
                    }
                }
            }
        }
        myParams.set($_SYS_EDITTABLEDATA,dataList);
        //设置重复数量
        myParams.sys().setCfsjl(editTableData.size()-dataList.size());
        Result r;
        if (errorList.size() > 0) {
            //设置错误信息相关参数
            MyParams sjsccwParams = LjqManager.jcxxByDxdm("SYS_LOG_SJSCCW",myParams.user());
            //设置为批量保存模式
            sjsccwParams.sys().setCllx(Cllx.plbc.name());
            //存入数据库
            sjsccwParams.set($_SYS_EDITTABLEDATA, errorList);
            ThreadPool.use().run(()->{
                //采用独立现场去执行错误数据入库，避免主业务事务影响
                Result r1 = LjqManager.data(sjsccwParams,Cllx.plbc.name());
                if (!r1.isStatus()) {
                    log.error("错误数据入库失败"+r1);
                }
            });
            r = failed("校验出了" + errorList.size() + "个错误，请按错误列表依次修改后再重新上传",errorList.size());
            r.setCode(XtZtm.SJPLSCJYBTG.getCode());
        }else {
            r = success("转换验证成功",dataList);
        }
        return r;
    }

    /**
     * 删除数据
     * @return 操作结果
     */
    public Result delete(MyParams myParams) {
        return LjqManager.data(myParams,Cllx.plsc.name());
    }

    public Result plsc(MyParams myParams) {
        if (Db.isSupported(getSjdx().getDxztlx())) {
            //数据库场景
            return plscDb(myParams);
        } else if (SjztLx.es.name().equals(getSjdx().getDxztlx())) {
            return plscEs(myParams);
        } else {
            return plscFile(myParams);
        }
    }

    private Result plscEs(MyParams myParams) {
        String zjzd = getSjdx().getZddmZjzd();
        if(isBlank(zjzd)){
            throw new MyException("主键字段没有配置，不能进行批量删除");
        }
        String yxxzd = getSjdx().getYxxzd();
        boolean wlsc = myParams.getBooleanValue("$.sys.wlsc");
        AtomicInteger wlscsl = new AtomicInteger();
        AtomicInteger ljscsl = new AtomicInteger();
        Result r = success("");
        //设置回调参数体
        myParams.set("$.sys.hdcst", (HdInterface) (list, hdjs) -> {
            if (isBlank(list)) {
                if (isBlank(r.getMsg())) {
                    r.setMsg("数据列表为空，没有需要处理的数据");
                }
                return r;
            }
            MyParams param = JsonUtil.clone(myParams);
            List<String> ids = new ArrayList<>();
            List<JSONObject> editTableData = new ArrayList<>();
            list.forEach(i -> {
                if (StringUtil.isNotBlank(yxxzd) && (wlsc || UserManager.hasAuth(param.user(), "QTQX_SYS"))) {
                    if ("0".equals(i.getString(yxxzd))) {
                        ids.add(i.getString(zjzd));
                        wlscsl.incrementAndGet();
                    } else {
                        i.set(yxxzd, "0");
                        editTableData.add(i);
                        ljscsl.incrementAndGet();
                    }
                } else if (isBlank(yxxzd)) {
                    ids.add(i.getString(zjzd));
                    wlscsl.incrementAndGet();
                } else {
                    i.set(yxxzd, "0");
                    editTableData.add(i);
                    ljscsl.incrementAndGet();
                }
                param.set($_SYS_IDS, ids);
                param.set($_SYS_EDITTABLEDATA, editTableData);
            });
            if (ids.size() != 0) {
                Result wlsc1 = ES.use(getSjdx().getDxzt()).plsc(param);
            }
            if (editTableData.size() != 0) {
                Map<String, Object> map = new HashMap<>();
                map.put("updateList", editTableData);
                Result ljsc = ES.use(getSjdx().getDxzt()).update(param, map);
                JSONArray updateIds = ljsc.getJSONArray("updateIds");
            }
            r.setMsg("物理删除记录数：" + wlscsl + "，" + "逻辑删除记录数：" + ljscsl);
            return r;
        });
        Result res = select(myParams);
        if(!res.isStatus()){
            return res;
        }
        return r;
    }

    /**
     * @return 核查结果，正确：true，错误：false
     */
    public Result inspect(MyParams myParams) {
        //不进行列表查询
        myParams.set("$.page.listRequired",false);
        Result r = LjqManager.select(myParams);
        if(!r.isStatus()){
            return r;
        }
        return success("核查成功",((PageInfo<JSONObject>)r.getData()).getTotalRow()>0);
    }


    @MyTransactional
    public Result select(MyParams myParams) {
        Result r;
        if (Db.isSupported(getSjdx().getDxztlx())) {
            //数据库场景
            r = selectDb(myParams);
        } else if(SjztLx.es.name().equals(getSjdx().getDxztlx())){
            r = selectEs(myParams);
        }else {
            r = selectFile(myParams);
        }
        return r;
    }


    @MyTransactional
    public Result insert(MyParams myParams) throws MyException {
        myParams.sys().setYzdjl(false);
        return LjqManager.save(myParams);
    }


    @MyTransactional
    public Result update(MyParams myParams) throws MyException{
        myParams.sys().setYzdjl(true);
        return LjqManager.save(myParams);
    }

    /**
     * 保存数据
     * @return 操作结果
     */
    @MyTransactional
    public Result save(MyParams myParams) {
        Result res = success("新增成功");
        if(myParams.sys().getYzdjl()){
            res.setMsg("更新成功");
            myParams.sys().setCllx(Cllx.update.name());
        }else{
            myParams.sys().setCllx(Cllx.insert.name());
        }
        Result r;
        if (Db.isSupported(getSjdx().getDxztlx())) {
            //数据库场景
            r = saveDb(myParams);
        } else if (SjztLx.es.name().equals(getSjdx().getDxztlx())) {
            //ES场景
            r = saveEs(myParams);
        } else {
            r = saveFile(myParams);
        }
        if(!r.isStatus()){
            throw r.newMyException();
        }
        if(valByDef(myParams.sys().getBatch(),false)){
            //批量操作时直接返回
            return r;
        }
        JSONObject data = new JSONObject();
        //设置操作记录数
        data.put("czjls",r.getData());
        //保存成功，返回主键信息
        data.put(getSjdx().getZjzd(),myParams.get("$.yobj." + getSjdx().getZddmZjzd()));
        //返回处理类型，针对自动判断处理类型时，告知前端时进行了插入还是更新
        data.put(SjsjField.cllx.name(), myParams.sys().getCllx());
        if(!isBlank(myParams.getJSONObject($_SYS_CSTCL))){
            //参数体处理的
            JSONObject cstcl = myParams.getJSONObject($_SYS_CSTCL);
            for(String key:cstcl.keySet()){
                MyParams nParams = new MyParams(cstcl.getJSONObject(key));
                MyParams nJcxx = LjqManager.jcxxByParams(nParams,myParams.user(), false);
                r = LjqManager.data(nJcxx,nParams.sys().getCllx());
                if(!r.isStatus()){
                    throw r.newMyException();
                }
                data.put(key,r.getData());
                res.addMsg(key+"："+r.getMsg());
            }
        }
        res.setData(data);
        return res;
    }

    /**
     * 批量保存
     * @param myParams 相关参数
     * @return 操作结果
     */
    @MyTransactional
    public Result plbc(MyParams myParams){
        Result r = plsjzhyz(myParams);
        if(!r.isStatus()){
            //验证不通过
            throw r.newMyException();
        }
        JSONArray editTableData = myParams.getJSONArray($_SYS_EDITTABLEDATA);
        JSONObject[] list;
        if(isBlank(editTableData)){
            //也谈不上错误吧。。
            return success("没有提供批量保存的数据");
        }else {
            list = editTableData.toJavaList(JSONObject.class).toArray(new JSONObject[]{});
        }
        if(myParams.sys().getBatch()==null){
            myParams.sys().setBatch(true);
        }
        if (Db.isSupported(getSjdx().getDxztlx())) {
            //数据库场景
            r = plbcDb(myParams,list);
        } else if (SjztLx.es.name().equals(getSjdx().getDxztlx())) {
            r = plbcEs(myParams,list);
        } else {
            r = plbcFile(myParams,list);
        }
        if(!r.isStatus()){
            //异常场景抛出异常，便于事务回滚
            throw r.newMyException();
        }
        return r;
    }

    private Result plbcEs(MyParams myParams, JSONObject[] list) {
        log.trace("开始进行ES批量保存");
        String sjzt = getSjdx().getDxzt();
        //新增数据列表
        List<JSONObject> insertlist = new ArrayList<>();
        //修改数据列表
        List<JSONObject> updateList = new ArrayList<>();
        AtomicInteger insert = new AtomicInteger();
        AtomicInteger update = new AtomicInteger();
        AtomicInteger ignore = new AtomicInteger();
        for (JSONObject j : list){
            myParams.set($_SYS_YZDJL, valByDef(j.remove("my-yzdjl"), false));
            if (myParams.getBooleanValue($_SYS_YZDJL) && myParams.getBooleanValue("$.sys.bzxgx")) {
                ignore.incrementAndGet();
            } else if (!myParams.getBooleanValue($_SYS_YZDJL) && myParams.getBooleanValue("$.sys.bzxxz")) {
                ignore.incrementAndGet();
            } else {
                myParams.put("obj", j.remove("my-obj"));
                if (myParams.getBooleanValue($_SYS_YZDJL)) {
                    updateList.add(j);
                    update.incrementAndGet();
                } else {
                    insertlist.add(j);
                    insert.incrementAndGet();
                }
            }
        }
        Map<String, Object> map = new HashMap<>();
        map.put("insertList", insertlist);
        map.put("updateList", updateList);
        //保存数据
        Result res = ES.use(sjzt).update(myParams, map);
        if (!res.isStatus()) {
            throw res.newMyException();
        }
//        PageInfo page = res.getData(PageInfo.class);
//        JSONArray insertIds = (JSONArray) page.get("insertIds");
//        JSONArray updateIds = (JSONArray) page.get("updateIds");
        return sjplcljg(list.length, insert.get(), update.get(), ignore.get(), myParams.getIntValue("$.sys.cfsjl"));
    }


    public Result getSql(MyParams myParams) {
        String cllx = myParams.sql().getCllx();
        if(isBlank(cllx)){
            cllx = myParams.sys().getCllx();
        }else {
            myParams.sql().setCllx(null);
        }
        SysSjglSjdx sjdx1 = myParams.getObject(KEY_SJDX, SysSjglSjdx.class);
        String sql;
        if(Cllx.select.name().equals(cllx)&&!isBlank(myParams.znjh().getSql())){
            //查询场景，存在外部直接传入的sql则优先使用，但要避免一般用户传入造成风险
            sql = myParams.znjh().getSql();
            String[] arr = Db.parseDictExp(sql, sjdx1.getDxzt());
            if(StringUtil.isNotBlank(myParams.page().getOrderBy())){
                arr[1] += " order by "+myParams.page().getOrderBy();
            }
            sql = arr[1]+";ds="+arr[0];
            myParams.sql().setDefaultSql(sql);
        }else {
            //清除之前的默认sql
            myParams.sql().setDefaultSql(null);
            //设置from
            String dxgs = StringUtil.fastNotNull(sjdx1.getDxgs(),sjdx1.getSjztObj().getDxgsNew());
            myParams.sql().setFrom((isBlank(dxgs) ? "" : dxgs + ".") + sjdx1.getJtdx());
            //先获取该处理类型对应的数据库的默认sql
            Db.getZdSqlTmpl(myParams,"DEFAULT."+cllx,sjdx1.getDxztlx());
            //处理父对象代码的场景
            if(StringUtil.isNotBlank(myParams.sys().getFsjdxdm())&&
                    !myParams.sys().getFsjdxdm().equalsIgnoreCase(sjdx1.getDxdm())){
                Db.getZdSqlTmpl(myParams,myParams.sys().getFsjdxdm()+"."
                        +cllx,sjdx1.getDxztlx());
            }
            //获取该对象的专有sql
            sql = Db.getZdSqlTmpl(myParams,sjdx1.getDxdm() + "." + cllx,sjdx1.getDxztlx());
            if(isBlank(sql)){
                //模板都不存在哎
                throw new MyException(Msg.msg("ljq.default.mypzgsql", cllx), sjdx1);
            }
        }
        log.trace("获取sql，{}.{}：{}",sjdx1.getDxdm(),cllx,sql);
        return success("获取sql成功",Db.parseDictExp(sql, sjdx1.getDxzt()));
    }

    /**
     * 导出模板
     * @param myParams 相关参数
     * @return 处理结果
     */
    public Result dcmb(MyParams myParams) {
        //导出数据
        String fileName = getSjdx().getDxmc()+"-数据上传模板-" + DateUtil.getGabDate();
        if (StringUtil.isNotBlank(myParams.sys().getDcwjm())) {
            fileName = myParams.sys().getDcwjm();
        }
        try {
            List<List<String>> header = new ArrayList<>();
            List<String> h;
            List<List<Object>> data = new ArrayList<>();
            List<Object> r = new ArrayList<>();
            data.add(r);
            List<String> zdList = new ArrayList<>();
            Map<String, SysSjglSjzd> fields = (Map<String, SysSjglSjzd>) myParams.get(KEY_FIELDS);
            for (Map.Entry<String, SysSjglSjzd> f : fields.entrySet()) {
                SysSjglSjzd field = f.getValue();
                if (valByDef(TypeUtils.castToBoolean(field.getMbzs()),false)) {
                    h = new ArrayList<>();
                    String btmc = field.getZdmc();
                    if(valByDef(myParams.getBoolean("$.sys.zddm"),true)){
                        //是否包含字段代码，默认为是
                        btmc += "[" + f.getKey() + "]";
                    }
                    zdList.add(f.getKey());
                    h.add(btmc);
                    header.add(h);
                    if(valByDef(TypeUtils.castToBoolean(field.getBjbt()),false)){
                        //必填
                        field.setDcbtbjs(IndexedColors.YELLOW.name());
                    }
                    r.add(VerifyRule.rulejx(field, field.getKzxxObj().getJSONObject("$.yzgz."+Cllx.insert.name())));
                }
            }
            //显示的字段
            myParams.other().setDcsjycl(Dcsjycl.builder().zdlist(zdList).build());
            return resultExcelFile(myParams,header, data, fileName);
        } catch (Exception e) {
            log.error("导出数据失败", e);
            throw new MyException("导出数据失败，请查看系统日志分析原因:" + e.getMessage());
        }
    }

    /**
     * websocket客户端注册事件
     */
    public Result wsmddzc(MyParams myParams){
        WebSocketKhdxxVo wsk = myParams.sys().getWskhdxx();
        log.info("{}客户端注册：{}", StringUtil.requireNonNull(myParams.user(),
                "注册WS的用户不能为空").getYhxm(),wsk);
        SysPtglXtxx xx = SysPtglXtxx.builder().xxnr(getSjdx().getDxmc() + "：消息监听注册成功")
                .mdddl(wsk.getMdddl()).mddxl(wsk.getMddxl()).tshh(wsk.getToken()).build();
        XtxxWebSocket.sendMsg(xx,myParams.user());
        return success("收到注册消息");
    }

    /**
     * websocket客户端取消事件
     */
    public Result wsmddqx(MyParams myParams){
        WebSocketKhdxxVo wsk = myParams.sys().getWskhdxx();
        log.info("{}客户端取消：{}", StringUtil.requireNonNull(myParams.user(),
                "取消注册WS的用户不能为空").getYhxm(),wsk);
        return success("收到取消消息");
    }

    /**
     * 穿透app调用，相当于一个代理整合，便于拆分整合应用，如hadoop相关可以单独拆分一个应用，再代理整合一起调用
     * @param myParams 参数
     * @return 结果
     */
    public Result ctapp(MyParams myParams) {
        String ctapp = myParams.sys().getCtapp();
        log.trace("开始进行穿透调用：{}",ctapp);
        //组装请求参数，拿出原始请求
        MyParams ysParams = myParams.other().getYsParams();
        String url = UserManager.doDesEncryptUrl(StringUtil.fastNotNull(myParams.sys().getCturl(),
                Conf.getUtilConfig().getZnjh().getUrl(),"/default"),ctapp,myParams.user().getYhdm());
        ysParams.sys().setXzms(Xzms.BASE64OBJ.getCode());
        //穿透调用时移除加密机制。
        ysParams.sys().setPublicKey(null);
//        if(!isBlank(myParams.getString($_SYS_CLIENT_IP))){
//            //ip加密，接收方才认，避免入侵
//            ysParams.set($_SYS_CLIENT_IP,StringUtil.encodeing(myParams.getString($_SYS_CLIENT_IP),"sm4ecb",))
//        }
        HttpUtil.HttpUtilBuilder hb = HttpUtil.builder();
        if(!isBlank(myParams.sys().getTimeout())){
            //设置超时参数
            hb.cssc(myParams.sys().getTimeout());
        }
        boolean sfyb = hdffToHdcst(ysParams,myParams);
        Result r = Result.parse(hb.build().josnByJson(url, ysParams.toJSONString()));
        return ybjgcl(myParams, r,sfyb);
    }

    /**
     * 统一回调方法
     */
    public Result tyhd(MyParams myParams) {
        String hdqqid = myParams.getString("$.sys.hdqqid");
        if(isBlank(hdqqid)){
            throw new MyException("没有提供回调请求id，无法确定对应哪一次请求");
        }
        int i = 0;
        while (i<10&&!defaultCache.containsKey(hdqqid)){
            //避免回调回来时，主请求还未返回
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                log.warn("等待异常");
            }
            i++;
        }
        if(!defaultCache.containsKey(hdqqid)){
            throw new MyException("没有对应等待回调的请求");
        }
        HdInterface hdff = (HdInterface) defaultCache.get(hdqqid);
        Result result = null;
        try {
            result = Result.parse(myParams.sys().getContext()+"");
            result.addMsg("回调方返回结果");
            Object r = myParams.get($_SYS_EDITTABLEDATA);
            List<JSONObject> list;
            if(isBlank(r)){
                return success("回调数据为空直接返回");
            }
            list = myParams.getJSONArray($_SYS_EDITTABLEDATA).toJavaList(JSONObject.class);
            return hdff.run(list,myParams.sys().getHdjs(),result);
        }finally {
            if(myParams.sys().getHdjs()){
                //回调结束，请求方法结束等待
                Lock lock = (Lock) defaultCache.get("lock_"+hdqqid);
                Condition cond = (Condition) defaultCache.get("cond_"+hdqqid);
                defaultCache.put("result_"+hdqqid,result);
                //唤醒等待
                lock.lock();
                cond.signal();
                lock.unlock();
            }
        }
    }

    /**
     * 穿透调用，可以实现将请求生成文件，传到其他地方调用后结果返回
     * @param myParams 参数
     * @return 结果
     */
    public Result ctdy(MyParams myParams) {
        //穿透调用
        String ctdyKey = "ctdy" + myParams.sys().getCtdy();
        SysZnjhCtdy ctdy = defaultCache.getObject(ctdyKey, SysZnjhCtdy.class);
        if(ctdy==null){
            ctdy = db(DEFAULT).lambdaQuery(SysZnjhCtdy.class)
                    .andEq(SysZnjhCtdy::getTsjd,Conf.getAppdm())
                    .andEq(SysZnjhCtdy::getDm, myParams.sys().getCtdy())
                    .singleSimple();
            //缓存一下，避免每次查询
            if(ctdy==null){
                ctdy = new SysZnjhCtdy();
            }
            defaultCache.put(ctdyKey,ctdy);
        }
        if (isBlank(ctdy.getDm())) {
            throw new MyException("没有找到穿透调用配置："+ myParams.getString("$.sys.ctdy"));
        }
        //组装请求参数，拿出原始请求
        MyParams ysParams = myParams.other().getYsParams();
        //设置来源交换节点，便于请求方法找传回结果的穿透
        ysParams.znjh().setMbjd(Conf.getAppdm());
        //设置请求用户
        ysParams.znjh().setUser(myParams.user().getYhdm());
        boolean sfyb = hdffToHdcst(ysParams,myParams);
        //生成穿透文件
        BasicSjzt tszt = BasicSjzt.useSjzt(ctdy.getTsjhObj().getSrzt());
        IFile tswj = new BdwjFile(FileUtil.getFilePath(ctdy.getTsjhObj().getSrml(),
                ctdy.getTsml(),"/ctdy"),myParams.sys().getQqid()+".json",tszt);
        try {
            log.debug("开始穿透调用：{}",tswj.getAbsolutePath());
            tszt.save(new ByteArrayInputStream(ysParams.toString().getBytes(StandardCharsets.UTF_8)),tswj);
        } catch (Exception e) {
            throw new MyException("保存穿透调用请求文件失败",e);
        }
        if(!valByDef(myParams.sys().getCtdyjg(),true)){
            //是否要穿透调用结果
            return success(ctdy.getMc()+">穿透调用完成，该穿透调用不需要穿透调用结果");
        }
        if(isBlank(ctdy.getJsjh())){
            log.debug("没有配置接收穿透结果的交换：{}",ctdy.getMc());
            return success(ctdy.getMc()+">穿透调用完成，没有配置接收穿透结果的交换");
        }
        //采用锁等待返回结果，超时时长配置
        Lock lock = new ReentrantLock();
        try {
            lock.lock();
            Condition cond = lock.newCondition();
            BasicFile.ctdyMap.put(IFile.RESULT+tswj.getName(),lock);
            BasicFile.ctdyCondMap.put(IFile.RESULT+tswj.getName(),cond);
            if(cond.await(StringUtil.fastNotNull(myParams.sys().getTimeout(),
                            Conf.getUtilConfig().getZnjh().getCtdydd(),120000),
                    TimeUnit.MILLISECONDS)){
                //结果返回
                Result r = BasicFile.ctjgMap.get(IFile.RESULT + tswj.getName());
                if(r == null){
                    return failed(ctdy.getMc()+">没有获取到穿透调用的结果");
                }
                r.addMsg(ctdy.getMc()+">穿透调用完成");
                return ybjgcl(myParams, r,sfyb);
            }else {
                Result r = failed(ctdy.getMc()+">穿透调用超时："+tswj.getName());
                r.setCode(XtZtm.CTDYCS.getCode());
                return r;
            }
        } catch (InterruptedException e) {
            throw new MyException(ctdy.getMc() + ">穿透调用被中断：" + tswj.getName()+"->"+e.getMessage(),e);
        }finally {
            lock.unlock();
            BasicFile.ctjgMap.remove(IFile.RESULT + tswj.getName());
            BasicFile.ctdyMap.remove(IFile.RESULT+tswj.getName());
            BasicFile.ctdyCondMap.remove(IFile.RESULT+tswj.getName());
        }
    }

    /**
     * 结果发送到前端
     * @param myParams 相关参数
     */

    public void sendResult(MyParams myParams){
        Result r = myParams.yobj(Result.class);
        HttpServletResponse response = LjqManager.getHttpServletResponse();
        //将请求id返回给前端
        r.setQqid(myParams.sys().getQqid());
        //非虚拟对象时才记录日志
        writeCzrz(myParams, r);
        //根据返回类型向前端推送数据
        if (XtZtm.DZCLJG.getCode() ==r.getCode()||response==null) {
            //前面自行处理，不统一处理，或响应对象为空
            log.trace("不统一处理返回信息：{}",r);
        } else if (HttpURLConnection.HTTP_MOVED_TEMP ==r.getCode()) {//重定向
            response.setStatus(r.getCode());
            try{
                response.sendRedirect(r.getData()+"");
            }catch (Exception e){
                log.error("重定向失败："+r,e);
                WebUtil.sendJson(response, failed("重定向失败："+r));
            }
        } else if (MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(r.getDateType())) {
            //文件下载场景
            SysSjglFile file = r.getData(SysSjglFile.class);
            if(file==null){
                //下载文件，没文件信息
                r.addMsg("下载文件没有获取到文件信息");
                throw r.newMyException();
            }
            byte[] bytes;
            if(StringUtil.isNotBlank(file.getBytes())){
                //提供的是二进制数据
                bytes = file.getBytes();
                file.setBytes(null);
            }else if(StringUtil.isNotBlank(file.getWjnr())){
                //提供的是base64数据
                bytes = Base64.getDecoder().decode(file.getWjnr());
                file.setWjnr(null);
            }else{
                throw new MyException("没获取到要下载的数据");
            }
            //base64
            String base64;
            //优先取前端传入的下载模式
            int xzms = valByDef(myParams.sys().getXzms(), file.getXzms());
            file.setXzms(xzms);
            switch (Xzms.getByCode(xzms)){
                case AWJLXXZ:
                case EJZ:
                    WebUtil.sendBytes(response, bytes, file);
                    break;
                case BASE64:
                    base64 = Base64.getEncoder().encodeToString(bytes);
                    WebUtil.sendText(response,base64);
                    break;
                case BASE64OBJ:
                    //转为base64后作为data以json对象返回
                    base64 = Base64.getEncoder().encodeToString(bytes);
                    file.setWjnr(base64);
                    //修改下载模式为按文件类型下载，便于后续再次进入时直接进行下载，针对代理下载文件场景
                    file.setXzms(Xzms.AWJLXXZ.getCode());
                    r.setData(file);
                    WebUtil.sendJson(response,r);
                    break;
                default:
                    throw new MyException("不支持的下载模式："+file.getXzms());
            }
        } else {
            //默认JSON
            response.setStatus(r.getCode());
            if(r.getCode()>510){
                response.setStatus(HttpURLConnection.HTTP_BAD_REQUEST);
            }
            if(StringUtil.isNotBlank(myParams.sys().getPublicKey())){
                //配置了公钥，则采用该公钥对数据进行加密
                WebUtil.sendText(response,StringUtil.encodeing(r.toString(),Jmfs.sm2,
                        null,myParams.sys().getPublicKey()));
            }else {
                WebUtil.sendJson(response, r);
            }
        }
    }

    /**
     * 获取用户信息
     * @param myParams 参数对象
     * @return 用户信息
     */
    public Result getUser(MyParams myParams) {
        return success("获取用户成功",UserManager.getUser(myParams));
    }

    /**
     * 权限过滤 <br/>
     * @param myParams 参数集
     * @throws QxException 没有权限将抛出异常
     * @author jingma
     */
    protected void auth(MyParams myParams) throws QxException {
        if(hasAuth(myParams,getCllx(myParams))){
            //没有用户信息或有权限
            return;
        }
        QxException e = new QxException("没有操作权限");
        e.setData(myParams.sys().getAuthCode()+"_"+getCllx(myParams));
        throw e;
    }

    /**
     * 是否有权限
     */
    protected boolean hasAuth(MyParams myParams,String cllx){
        return UserManager.hasAuth(myParams,cllx);
    }

    /**
     * 保存数据-数据库 <br/>
     * @param myParams 相关参数
     * @return 处理结果
     * @author jingma
     */
    protected Result saveDb(MyParams myParams) {
        String[] arr = (String[]) getSql(myParams).getData();
        if(valByDef(myParams.sys().getBatch(),false)){
            SQLManager sm = sqlManager(arr[0]);
            SqlId sqlId = sm.getSqlIdFactory().buildTemplate(arr[1]);
            SQLSource source = sm.getSqlLoader().queryAutoSQL(sqlId);
            if (source == null) {
                source = new SQLSource(sqlId, arr[1]);
                source.setSqlType(SQLType.UPDATE);
                sm.getSqlLoader().addSQL(sqlId, source);
            }

            ExecuteContext executeContext = ExecuteContext.instance(sm).initSQLSource(source);
            SQLExecutor script = sm.getDbStyle().buildExecutor(executeContext);
            script.beforeExecute(null,myParams,true);
            return success("数据库保存进行批量操作，此处只返回sql和参数信息，不执行sql",script.run(myParams));
        }
        try {
            return success("数据库保存成功", db(arr[0]).update(arr[1], myParams));
        }catch (Throwable t){
            throw new MyException("保存失败",t);
        }
    }

    private Result saveEs(MyParams myParams) {
        log.trace("开始进行ES新增");
        JSONArray arr = new JSONArray();
        JSONObject yobj = myParams.getJSONObject(KEY_YOBJ);
        yobj.put("my-yzdjl", myParams.getString($_SYS_YZDJL));
        arr.add(yobj);
        myParams.set($_SYS_EDITTABLEDATA, arr);
        return plbc(myParams);
    }
    /**
     * 保存数据-本地文件
     */
    protected Result saveFile(MyParams myParams) {
        return plbcFile(myParams,new JSONObject[]{myParams.getJSONObject(KEY_YOBJ)});
    }

    /**
     * 批量保存数据到本地文件，不考虑内容，原样保存
     */
    protected Result plbcFile(MyParams myParams, JSONObject[] list) {
        dcsjYcl(myParams,Arrays.asList(list), Cllx.insert.name());
        if(isBlank(myParams.getString("$.sys.fileType"))){
            //若外部没有要求文件格式，则采用对象类型
            myParams.set("$.sys.fileType",getSjdx().getDxlx());
        }
        Result r = resultExcelFile(myParams, myParams.other().getDcsjycl().getHeader(),
                myParams.other().getDcsjycl().getData(),
                getSjdx().getJtdx()+"_"+System.currentTimeMillis());
        SysSjglFile jo = r.getData(SysSjglFile.class);
        try {
            return new Result(BasicSjzt.useSjzt(getSjdx().getDxzt()).save(
                    new ByteArrayInputStream(jo.getBytes()),
                    new BdwjFile(getSjdx().getDxgs(),jo.getWjm())),"完成批量保存");
        } catch (Exception e) {
            throw new MyException("批量保存文件异常",e);
        }
    }

    protected Result plbcDb(MyParams myParams, JSONObject[] list) {
        log.trace("开始进行数据库批量保存："+list.length);
        BeanProcessor bp = getSqlManager().getDefaultBeanProcessors();
        ExecuteContext ec = ExecuteContext.instance(getSqlManager());
        ec.target = JSONObject.class;
        //批量更新
        if(myParams.sys().getBatch()) {
            return getSqlManager().executeOnConnection(new OnConnection<Result>() {

                public Result call(Connection conn) throws SQLException {
                    log.trace("存在的数据已查询完成，开始进行批量保存");
                    //获取事务提交量
                    int swtjl = myParams.sys().getSwtjl();
                    //总共多少条记录
                    int count = 0;
                    //插入多少记录
                    int insert = 0;
                    //更新多少记录
                    int update = 0;
                    //忽略多少记录
                    int ignore = 0;
                    Map<String,PreparedStatement> pm = new HashMap<>();
                    PreparedStatement p;
                    for (JSONObject j : list) {
                        count++;
                        //取消原来的处理类型，由保存方法自动判断
                        myParams.sys().setCllx("");
                        myParams.put(KEY_YOBJ, j);
                        try {
                            myParams.sys().setYzdjl(TypeUtils.castToBoolean(valByDef(j.remove("my-yzdjl"),false)));
                            if(myParams.sys().getYzdjl()&&myParams.sys().isBzxgx()){
                                ignore++;
                                continue;
                            }else if(!myParams.sys().getYzdjl()&&myParams.sys().isBzxxz()){
                                ignore++;
                                continue;
                            }
                            myParams.put(KEY_OBJ,j.remove("my-obj"));
                            SQLResult sr = save(myParams).getData(SQLResult.class);
                            p = pm.get(sr.jdbcSql);
                            if(p==null){
                                p = conn.prepareStatement(sr.jdbcSql);
                                pm.put(sr.jdbcSql,p);
                            }
                            bp.setPreparedStatementPara(ec,p,sr.jdbcPara);
                            p.addBatch();
                        }catch (MyException e){
                            throw new MyException(e.getMessage()+"，第" + (count + 1) + "行",e.getCode(),e.getData());
                        }
                        if(myParams.getBooleanValue($_SYS_YZDJL)){
                            update++;
                        }else{
                            insert++;
                        }
                        if (count % swtjl == 0) {
                            log.trace("开始执行批量入库提交");
                            for(Map.Entry<String,PreparedStatement> e:pm.entrySet()){
                                e.getValue().executeBatch();
                                e.getValue().close();
                            }
                            pm.clear();
                            log.info(getSjdx().getDxmc()+"批量保存已处理数据："+count);
                        }
                    }
                    log.trace("开始执行批量入库最后提交");
                    for(Map.Entry<String,PreparedStatement> e:pm.entrySet()){
                        e.getValue().executeBatch();
                        e.getValue().close();
                    }
                    pm.clear();
                    //重复量
                    return sjplcljg(list.length, insert, update, ignore, myParams.sys().getCfsjl());
                }
            });
        }else {
            //插入多少记录
            int insert = 0;
            //更新多少记录
            int update = 0;
            //忽略多少记录
            int ignore = 0;
            //非批量处理
            for (JSONObject j : list) {
                //取消原来的处理类型，由保存方法自动判断
                myParams.put(KEY_YOBJ, j);
                myParams.sys().setYzdjl(TypeUtils.castToBoolean(valByDef(j.remove("my-yzdjl"), false)));
                if(myParams.sys().getYzdjl()&&myParams.sys().isBzxgx()){
                    ignore++;
                    continue;
                }else if(!myParams.sys().getYzdjl()&&myParams.sys().isBzxxz()){
                    ignore++;
                    continue;
                }
                myParams.put(KEY_OBJ, j.remove("my-obj"));
                Result r;
                if(myParams.sys().getYzdjl()){
                    r = update(myParams);
                    update++;
                }else{
                    r = insert(myParams);
                    insert++;
                }
                if(!r.isStatus()){
                    throw r.newMyException();
                }
            }
            //重复量
            return sjplcljg(list.length, insert, update, ignore, myParams.sys().getCfsjl());
        }
    }

    @NotNull
    protected Result selectDb(MyParams myParams) {
        log.trace("开始进行数据库查询");
        //获取分页对象
        PageInfo<JSONObject> page = myParams.getObject(KEY_PAGE,PageInfo.class);
        //获取sql
        String[] arr = LjqManager.getSql(myParams, Cllx.select.name());
        String sql;
        if (StringUtil.isNotBlank(getSjdx().getSqlmb())) {
            //对象上设置了查询sql模板
            sql = TmplUtil.buildStrSql(getSjdx().getSqlmb(), myParams).trim();
            if (sql.startsWith("error:")) {
                throw new MyException(sql.substring("error:".length()));
            }
            arr = Db.parseDictExp(sql, arr[0]);
        }
        //排序信息在sql模板中处理
        page.setOrderBy("");
        log.trace("查询数据库及语句：{},{}",arr[0],arr[1]);
        String[] sqlArr = arr[1].split(";");
        for (int i =0;i<sqlArr.length-1;i++){
            //前面的sql只能执行更新
            db(arr[0]).update(sqlArr[i],myParams);
        }
        arr[1] = sqlArr[sqlArr.length-1];
        if(myParams.get($_SYS_HDCST)==null){
            //分页查询
            page = db(arr[0]).queryPage(page, arr[1], myParams);
            for(JSONObject row : page.getList()){
                selectClRow(myParams,row);
            }
            return success("数据库查询成功",page);
        }else {
            //有回调参数体，采用流式读取
            //开启事务，采用流式读取必须开启事务
            HdInterface hdff = getHdff(myParams);
            try {
                Result r = success("");
                AtomicLong num = new AtomicLong();
                List<JSONObject> list = new ArrayList<>();
                PageInfo<JSONObject> finalPage = page;
                SQLManager sm = sqlManager(arr[0]);
                SqlId sqlId = sm.getSqlIdFactory().buildTemplate(arr[1]);
                SQLSource source = sm.getSqlLoader().queryAutoSQL(sqlId);
                if (source == null) {
                    source = new SQLSource(sqlId, arr[1]);
                    source.setSqlType(SQLType.SELECT);
                    sm.getSqlLoader().addSQL(sqlId, source);
                }

                ExecuteContext executeContext = ExecuteContext.instance(sm).initSQLSource(source);
                SQLExecutor script = sm.getDbStyle().buildExecutor(executeContext);
                script.beforeExecute(null,myParams,false);
                SQLResult sr = script.run(myParams);
                log.debug("数据库查询sql：{}，{}",sr.jdbcSql,JSON.toJSONString(sr.jdbcPara));
                BeanProcessor bp = getSqlManager().getDefaultBeanProcessors();
                ExecuteContext ec = ExecuteContext.instance(getSqlManager());
                ec.target = JSONObject.class;
                sm.executeOnConnection(new OnConnection<Object>() {

                    public Object call(Connection conn) throws SQLException {
                        PreparedStatement p = conn.prepareStatement(sr.jdbcSql);
                        bp.setPreparedStatementPara(ec,p,sr.jdbcPara);
                        ResultSet rs = p.executeQuery();
                        ResultSetMetaData meta = rs.getMetaData();
                        JSONObject row;
                        while (rs.next()){
                            row = new JSONObject();
                            for(int i = 1;i<=meta.getColumnCount();i++){
                                //优先获取别名
                                row.put(meta.getColumnLabel(i).toLowerCase(),rs.getString(i));
                            }
                            if(list.size()==0){
                                log.debug("数据库查询数据样例：{}",row);
                            }
                            num.getAndIncrement();
                            //处理每一行数据
                            selectClRow(myParams,row);
                            list.add(row);
                            if(list.size() == finalPage.getPageSize()){
                                r.addMsg(hdff.run(list,false).getMsg());
                                //处理完成，清空
                                list.clear();
                            }
                        }
                        return null;
                    }
                });
//                page.setList(list);
                page.setTotalRow(num.get());
                r.setData(page);
                r.addMsg("流式读取数据完成，数据已采用回调机制处理完成："+num);
                //最后再次处理
                r.addMsg(hdff.run(list,true,r).getMsg());
                return r;
            }catch (Throwable t){
                //处理异常结束回调
                hdff.run(null,true,failed("流式读取数据失败："+t.getMessage()));
                throw new MyException("流式读取数据失败",t);
            }
        }
    }

    protected Result selectEs(MyParams myParams) {
        log.trace("开始进行ES查询");
        JSONObject r = new JSONObject();
        SysSjglSjdx sjdx = myParams.getObject(KEY_SJDX, SysSjglSjdx.class);
        final PageInfo<JSONObject> page = (PageInfo) myParams.getObject("page", PageInfo.class);
        String[] arr = LjqManager.getSql(myParams,Cllx.select.name());
        if (StringUtil.isNotBlank(getSjdx().getSqlmb())) {
            String sql = TmplUtil.buildStrSql(getSjdx().getSqlmb(), myParams).trim();
            if (sql.startsWith("error:")) {
                throw new MyException(sql.substring("error:".length()));
            }
            arr = Db.parseDictExp(sql, arr[0]);
        }
        PageInfo res;
        if (myParams.get("$.sys.hdcst") != null) {
            final HdInterface hdff = this.getHdff(myParams);
            int pageNumber = 1;
            String cursor = "";
            List<JSONObject> columns;
            while (true) {
                page.setPageNumber(pageNumber);
                try {
                    res = ES.use(getSjdx().getDxzt()).queryPage(page, arr[1], myParams);
                    //分页查询会返回游标,第二次循环直接使用上一次循环返回的游标
                    if (!isBlank(res.get("cursor"))) {
                        cursor = res.get("cursor").toString();
                    }
                    columns = (List<JSONObject>) res.get("columns");
                    myParams.set("$.sys.cursor", cursor);
                    myParams.set("$.sys.columns", columns);
                    List<JSONObject> list = res.getList();
                    if (list.size() == 0) {
                        hdff.run(list, true);
                        break;
                    } else {
                        hdff.run(list, false);
                        if (isBlank(cursor) && pageNumber == 1) {
                            break;
                        }
                        pageNumber++;
                    }
                } catch (Throwable t) {
                    hdff.run(null,true,failed("流式读取数据失败："+t.getMessage()));
                    throw new MyException("查询异常：" + t.getMessage(), t);
                }
            }
        } else {
            res = ES.use(getSjdx().getDxzt()).queryPage(page, arr[1], myParams);
            // 字典翻译
            Iterator var5 = res.getList().iterator();
            while (var5.hasNext()) {
                JSONObject row = (JSONObject) var5.next();
                this.selectClRow(myParams, row);
            }
            //查询完后去掉结果对象中临时存储的游标与字段
            r = JSONObject.parseObject(JSON.toJSONString(res));
            r.set("tails", "{}");
        }
        return success("请求成功", r.toJavaObject(PageInfo.class));
    }

    /**
     * 获取查询回调方法
     */
    protected HdInterface getHdff(MyParams myParams) {
        HdInterface hdff;
        if(myParams.get($_SYS_HDCST) instanceof HdInterface){
            hdff = (HdInterface) myParams.get($_SYS_HDCST);
        }else {
            MyParams hdcst = new MyParams(myParams.getJSONObject($_SYS_HDCST));
            //设置回调请求id
            hdcst.sys().setHdqqid(myParams.sys().getQqid());
            MyParams jcxx = LjqManager.jcxxByParams(JsonUtil.clone(hdcst),myParams.user());
            //设置下载模式，处理文件传输场景
            jcxx.sys().setXzms(Xzms.BASE64OBJ.getCode());
            hdff = new HdInterface() {

                public Result run(List<JSONObject> list, boolean hdjs) {
                    return run(list,hdjs,success("成功获取数据"));
                }


                public Result run(List<JSONObject> list, boolean hdjs,Result r) {
                    jcxx.sys().setEditTableData(list);
                    jcxx.other().getYsParams().sys().setEditTableData(list);
                    //告知请求方法，是否回调结束
                    jcxx.sys().setHdjs(hdjs);
                    jcxx.other().getYsParams().sys().setHdjs(hdjs);
                    //设置调消息
                    jcxx.sys().setContext(r);
                    jcxx.other().getYsParams().sys().setContext(r);
                    Result result = LjqManager.data(jcxx,jcxx.sys().getCllx());
                    if (result.getCode() == HttpURLConnection.HTTP_OK) {
                        //正常状态
                        log.info("查询回调完成："+result);
                    }else if(result.getCode() == XtZtm.CTDYCS.getCode()){
                        //穿透调用超时
                        log.info("穿透调用超时按正常处理，不再重复推送："+result);
                    }else {
                        throw result.newMyException();
                    }
                    return result;
                }
            };
        }
        return hdff;
    }

    /**
     * 查询-处理查询出的每一行数据
     */
    protected void selectClRow(MyParams myParams,JSONObject row) {
        //树形结构时，将是否有子节点的标志转为boolean形
        Map<String, SysSjglSjzd> fields = (Map<String, SysSjglSjzd>) myParams.get(KEY_FIELDS);
        String hasChild = myParams.getString("$.cllxkz['select'].tree.hasChild");
        String checkField = myParams.getString("$.cllxkz['select'].checkboxConfig.checkFieldOld");
        for(String zddm: fields.keySet()){
            if(StringUtil.isBlank(row.getString(zddm))){
                //字段值为空时跳过
                continue;
            }
            ZdKjlx kjlx = ZdKjlx.getByCode(fields.get(zddm).getKjlx());
            if(kjlx==null){
                continue;
            }
            //对结果进行字典翻译，字典或者级联控件
            switch (kjlx){
                case Select:
                case Radio:
                case ElCascader:
                case Checkbox:
                    row.put(zddm,row.getString(zddm));
                    row.put(zddm+"Mc",DictManager.zdMcByMoreDm(fields.get(zddm).getZdzdlb(), row.getString(zddm)));
                    //字典对象一起返回前端，为便于前端获取附加信息
//                    row.put(zddm+"_zdobj",DictManager.zdObjByMoreDm(fields.getString("$."+zddm+".zdzdlb"),row.getString(zddm)));
                    break;
                case Switch:
                    row.put(zddm,row.getString(zddm));
                    row.put(zddm+"Mc",DictManager.zdMcByDm(Zdlb.SYS_COMMON_LJPD.name(), row.getString(zddm)));
            }
        }
        if(hasChild !=null){
            row.put(hasChild, row.getBoolean(hasChild));
        }
        if(checkField !=null){
            row.put(checkField +"_boolean", row.getBoolean(checkField));
        }
    }
    /**
     * 查询数据-本地文件
     */
    protected Result selectFile(MyParams myParams) {
        myParams.sys().setCllx("selectFile");
        List<IFile> ilist = BasicSjzt.useSjzt(getSjdx().getDxzt()).listFiles(getSjdx());
        PageInfo<JSONObject> page = myParams.getObject(KEY_PAGE,PageInfo.class);
        Result r;
        if(myParams.get($_SYS_HDCST)==null){
            if(page.getPageNumber()>ilist.size()){
                return failed("页大小超出文件数量");
            }
            IFile file = ilist.get((int) page.getPageNumber()-1);
            //采用流式处理，避免全部读取到内存
            SjdxExcelReader er = new SjdxExcelReader(getSjdx(), myParams, file.toSjglFile());
            r = er.disposeExcel();
            if (!r.isStatus()) {
                return r;
            }
            //一个文件的数据
            if(er.getResult().size()>page.getPageSize()){
                page.setList(er.getResult().toJavaList(JSONObject.class).subList(0,page.getPageSize()));
            }else {
                page.setList(er.getResult().toJavaList(JSONObject.class));
            }
            //一个文件一页
            page.setTotalRow((long) page.getPageSize() *ilist.size());
            //分页查询
            for(JSONObject row : page.getList()){
                selectClRow(myParams,row);
            }
            return success("文件查询成功",page);
        }else {
            //有回调参数体，采用流式读取
            HdInterface hdff = getHdff(myParams);
            boolean scywj = myParams.getBooleanValue("$.sys.scywj");
            int num = 0;
            int i=0;
            for (IFile file : ilist) {
                i++;
                //采用流式处理，避免全部读取到内存
                SjdxExcelReader er = new SjdxExcelReader(getSjdx(), myParams, file.toSjglFile());
                r = er.disposeExcel();
                if (!r.isStatus()) {
                    return r;
                }
                num += er.getResult().size();
                //最后再次处理
                hdff.run(er.getResult().toJavaList(JSONObject.class),i>=ilist.size());
                //处理完成后删除
                if(scywj){
                    try {
                        file.delete();
                    } catch (Exception e) {
                        throw new MyException("删除源文件失败",e);
                    }
                }
            }
            return success("流式读取数据完成，数据已采用回调机制处理完成："+num);
        }
    }

    /**
     * 批量删除-数据库
     */
    @NotNull
    protected Result plscDb(MyParams myParams) {
        Result r;
        // 先调用一次查询语句，获取默认查询条件
        LjqManager.getSql(myParams,Cllx.select.name());
        boolean wlsc = myParams.getBooleanValue("$.sys.wlsc");
        r = success("");
        if (StringUtil.isNotBlank(getSjdx().getYxxzd()) && (wlsc||UserManager.hasAuth(myParams.user(),
                SysAuth.QTQX_SYS.name()))) {
            //存在有效性字段，且允许物理删除或有最高权限
            r = wlscByYxx(myParams);
        }
        String[] arr = LjqManager.getSql(myParams,Cllx.plsc.name());
        //后续继续进行逻辑删除操作
        int scs = db(arr[0]).update(arr[1], myParams);
        if (isBlank(getSjdx().getYxxzd())||isBlank(r.getMsg())) {
            r.addMsg("处理记录数：" + scs);
        } else {
            r.addMsg("逻辑删除记录数：" + scs);
        }
        return r;
    }

    /**
     * 批量删除-文件
     */
    protected Result plscFile(MyParams myParams) {
        BasicSjzt sjzt = BasicSjzt.useSjzt(getSjdx().getDxzt());
        List<IFile> ilist = BasicSjzt.useSjzt(getSjdx().getDxzt()).listFiles(getSjdx());
        try {
            for(IFile f:ilist){
                sjzt.delete(f);
            }
            return success("删除文件数："+ilist.size());
        }catch (Exception e){
            log.error("文件删除失败",e);
            throw new MyException("删除文件失败："+e.getMessage(),e);
        }
    }

    /**
     * 导出数据预处理,处理结果在系统参数myParams中。<br/>
     * 表头：$_OTHEROBJ_DCSJYCL_HEADER、数据：$_OTHEROBJ_DCSJYCL_DATA
     * @param myParams 系统参数
     * @param list 要导出的数据
     * @param cllx 处理类型
     */
    protected void dcsjYcl(MyParams myParams, List<Object> list, String cllx){
        //导出排除字段列表
        Set<String> dcpczdlb = isBlank(myParams.sys().getDcpczdlb())?new HashSet<>()
                :new HashSet<>(myParams.sys().getDcpczdlb());
        //导出字段列表
        List<String> dczdlb = isBlank(myParams.sys().getDczdlb())?new ArrayList<>()
                :myParams.sys().getDczdlb();
        Map<String, SysSjglSjzd> fields = (Map<String, SysSjglSjzd>) myParams.get(KEY_FIELDS);
        if(isBlank(dczdlb)){
            for (Map.Entry<String, SysSjglSjzd> f : fields.entrySet()) {
                SysSjglSjzd field = f.getValue();
                ZdKjlx kjlx = ZdKjlx.getByCode(field.getKjlx());
                if (valByDef(field.getKzxxObj().getBoolean("$.cllxkz."+cllx+".show"),false)
                        && !dcpczdlb.contains(f.getValue().getZdmc())
                        && (kjlx==null||(!kjlx.equals(ZdKjlx.Password) && !kjlx.equals(ZdKjlx.Buttons)
                        && !kjlx.equals(ZdKjlx.MyGroup) && !kjlx.equals(ZdKjlx.MySelectGrid)))) {
                    dczdlb.add(f.getKey());
                }
            }
        }
        List<List<String>> header = new ArrayList<>();
        List<String> h;
        for(String dczd : dczdlb){
            h = new ArrayList<>();
            //详情展示且前端没有要求不导出且控件类型不是密码的字段才导出
            h.add(fields.get(dczd).getZdmc());
            header.add(h);
        }
        //增加行高
        boolean zjhg = false;
        List<List<Object>> data = new ArrayList<>();
        List<Object> rows;
        for (Object r2 : list) {
            JSONObject o;
            if(r2 instanceof JSONObject){
                o = (JSONObject) r2;
            }else if(r2 instanceof BasicBean){
                o = ((BasicBean) r2).toJSONObject();
            }else {
                throw new MyException("不支持导出的数据类型："+r2.getClass().getName());
            }
            rows = new ArrayList<>();
            data.add(rows);
            for (String f : dczdlb) {
                SysSjglSjzd field = fields.get(f);
                String val = o.getString(f);
                ZdKjlx kjlx = ZdKjlx.getByCode(field.getKjlx());
                if(kjlx!=null){
                    switch (kjlx){
                        case Switch:
                            fields.get(f).setZdzdlb(Zdlb.SYS_COMMON_LJPD.name());
                        case Select:
                        case Checkbox:
                        case Radio:
                        case ElCascader:
                            rows.add(DictManager.zdMcByMoreDm(fields.get(f).getZdzdlb(), val));
                            break;
                        case ElDatePicker:
                            //这个格式可定制
                            String fmt = valByDef(field.getKzxxObj().getString("$.kjkz.zsgs"),DateUtil.DATE_FORMATTER_L);
                            String v = DateUtil.doFormatDate(val, fmt);
                            if (StringUtil.isBlank(v)) {
                                v = val;
                            }
                            rows.add(v);
                            break;
                        case MyImage:
                            //图片
                            rows.add(ImageConverterKey.builder().lj(val).build());
                            //有图片增加行高
                            zjhg = true;
                            break;
                        default:
                            //EXCEL限制单个大小
                            if (StringUtil.isNotBlank(val)) {
                                val = val.length() > 32767 ? val.substring(0, 32767) : val;
                            }
                            rows.add(val);
                    }
                }else {
                    //EXCEL限制单个大小
                    if (StringUtil.isNotBlank(val)) {
                        val = val.length() > 32767 ? val.substring(0, 32767) : val;
                    }
                    rows.add(val);
                }
            }
        }
        Dcsjycl dcsjycl = new Dcsjycl();
        myParams.other().setDcsjycl(dcsjycl);
        dcsjycl.setHeader(header);
        dcsjycl.setData(data);
        //显示的字段
        dcsjycl.setZdlist(dczdlb);
        //显示的字段
        dcsjycl.setZjhg(zjhg);
    }

    /**
     * 基于有效性的物理删除 <br/>
     * @param myParams 相关参数
     * @return 处理结果
     * @author jingma
     */
    protected Result wlscByYxx(MyParams myParams) {
        //有有效性字段
        String[] arr = LjqManager.getSql(myParams, Cllx.wlplsc.name());
        //先执行物理删除，将本次删除中，已经为无效的进行物理删除
        int scs = db(arr[0]).update(arr[1], myParams);
        return success("物理删除数：" + scs);
    }

    /**
     * 设置obj <br/>
     * @param myParams 参数
     * @param batch 是否批量处理，批量处理则值返回条件，不进行数据库查询
     * @author jingma
     */
    protected Result putObj(MyParams myParams,boolean batch) {
        myParams.set($_SYS_YZDJL, Boolean.FALSE);
        JSONObject yobj = myParams.getJSONObject(KEY_YOBJ);
        if(isBlank(yobj)){
            return success("没有表单数据");
        }
        JSONObject qcObj = new JSONObject();
        Map<String ,SysSjglSjzd> qcList = (Map<String ,SysSjglSjzd>) fieldsCache.get(getSjdx().getId()+ QCZD_LIST);
        if(StringUtil.requireNonNull(qcList,"去重字段列表为空，奇葩").size()==0){
            return success("没有去重字段");
        }else if(!isBlank(getSjdx().getZjzd())&&StringUtil.isNotBlank(yobj.get(getSjdx().getZjzd()))){
            //存在主键，只认主键
            qcObj.put(getSjdx().getZddmZjzd(),yobj.getString(getSjdx().getZddmZjzd()));
        }else if(qcList.size()==1){
            //一个去重字段
            SysSjglSjzd f = (SysSjglSjzd) qcList.values().toArray()[0];
            if (f!=null&&StringUtil.isNotBlank(yobj.getString(f.getZddm()))) {
                //该去重字段不为空
                qcObj.put(f.getZddm(),yobj.getString(f.getZddm()));
            }
        }else if(StringUtil.isNotBlank(getSjdx().getQczd())){
            //多个去重字段，且设置了去重字段
            StringBuilder key = new StringBuilder();
            for(SysSjglSjzd f : qcList.values()){
                //将去重字段的值拼接
                key.append(yobj.getString(f.getZddm())).append("_");
            }
            //对去重字段进行md5编码
            qcObj.put(getSjdx().getZddmQczd(), MD5Util.encode(key.toString()));
            //对原对象进行去重字段值补充，便于后续入库
            yobj.put(getSjdx().getZddmQczd(), MD5Util.encode(key.toString()));
        }else{
            throw new MyException("配置了具体的多个去重字段，但没有在数据对象中配置存储去重字段md5值的“去重字段”："+qcList.size());
        }
        if (qcObj.size()>0) {
            if(batch){
                //批量处理，此处不执行
                return success("批量处理",qcObj);
            }
            //存在去重字段
            //不进行总量查询
            myParams.set("$.page.totalRequired", Boolean.FALSE);
            //设置根据去重字段查询数据的条件
            myParams.put(KEY_YOBJ,qcObj);
            Result r;
            try{
                r = select(myParams);
                if(r.isStatus()){
                    PageInfo page = r.getData(PageInfo.class);
                    if (page.getList().size() == 1) {
                        //标记能找到要修改的对象，没找到可能是不存在，也可能是没有权限，避免修改无权限记录
                        myParams.set($_SYS_YZDJL, Boolean.TRUE);
                        Object obj = page.getList().get(0);
                        myParams.put(KEY_OBJ, obj);
                        setYobjByObj(yobj, qcList, obj);
                        if(Cllx.insert.name().equals(getCllx(myParams))){
                            //新增的数据通过去重字段查询到值，表示重复数据，改为更新
                            myParams.sys().setCllx(Cllx.update.name());
                        }
                    }
                }
            }catch (MyException e){
                log.trace("根据去重信息读取数据失败",e);
            }
            //还原原来的输入参数
            myParams.put(KEY_YOBJ,yobj);
        }
        return success("完成");
    }

    /**
     * 基于从库中读取的记录信息设置外部输入的表单
     * @param yobj 外部输入的表单
     * @param qczdMap 去重字段
     * @param obj 库中读取的记录
     */
    protected void setYobjByObj(JSONObject yobj, Map<String ,SysSjglSjzd> qczdMap, Object obj) {
        if(!isBlank(getSjdx().getZjzd())&&StringUtil.isBlank(yobj.getString(getSjdx().getZjzd()))){
            yobj.put(getSjdx().getZddmZjzd(), ClassUtil.getVal(getSjdx().getZddmZjzd(),null,obj));
        }
        if(StringUtil.isNotBlank(getSjdx().getQczd())&& qczdMap.size()>1){
            //多个去重字段，且设置了去重字段
            StringBuilder key = new StringBuilder();
            for(SysSjglSjzd f : qczdMap.values()){
                //将去重字段的值拼接，优先取前端传入的值，不存在则取库中的值
                key.append(StringUtil.fastNotNull(yobj.getString(f.getZddm()),
                        ClassUtil.getVal(f.getZddm(),null,obj),NULL_STR)).append("_");
            }
            //对原对象进行去重字段值补充，便于后续入库
            yobj.put(getSjdx().getZddmQczd(), MD5Util.encode(key.toString()));
        }
    }

    /**
     * 获取数据字段 <br/>
     *
     * @author jingma
     */
    protected synchronized void getFields(MyParams myParams, SysQxYhxx user) {
        //基于对象和用户等级缓存对象字段
        String cacheKey = getSjdx().getId();
        if (user != null) {
            cacheKey = getSjdx().getId() + user.getYhdj();
        }
        Object obj = fieldsCache.get(cacheKey);
        Map<String, SysSjglSjzd> fields;
        if (obj != null && !myParams.sys().getClearCache()) {
            //没有缓存且没有要求清除缓存
            fields = (Map<String, SysSjglSjzd>) obj;
        }else{
            fields = new LinkedHashMap<>();
            String[] r = LjqManager.getSql(myParams, Cllx.getFields.name());
            List<SysSjglSjzd> zdList = sqlManager(r[0]).execute(r[1],SysSjglSjzd.class, myParams);
            if(isBlank(zdList)){
                throw new MyException("该数据对象没有配置字段："+myParams.sjdx().getDxdm());
            }
            for(SysSjglSjzd zd:zdList){
                fields.put(zd.getZddm(),zd);
            }
            fieldsInit(fields, myParams);
            if(myParams.containsKey(KEY_FIELDS)){
                //如果系统配置的有默认字段则合并,具体字段配置优先
                Map<String, SysSjglSjzd> sfields = myParams.fields();
                sfields.forEach((s, o) -> {
                    if(isBlank(o.getZddm())){
                        //配置的公共字段自动补充一些默认信息
                        o.setZddm(s);
                    }
                    if(isBlank(o.getZdywlb())){
                        //默认业务类别
                        o.setZdywlb(ZdYwlb.XNZD.getCode());
                    }
                });
                JSONObject sf = new JSONObject();
                sf.putAll(sfields);
                JsonUtil.mergeJSONObject(sf,fields);
                //根据排序字段进行排序
                List list = new ArrayList(sf.values());
                list.sort(Comparator.comparingInt(o -> valByDef(((SysSjglSjzd)o).getPx(),0)));
                fields = new LinkedHashMap<>();
                for(Object o: list){
                    SysSjglSjzd f = (SysSjglSjzd) o;
                    fields.put(f.getZddm(), f);
                }
            }
            List<SysSjglSjzd> qcList = new ArrayList<>();
            for(SysSjglSjzd f: fields.values()){
                if(valByDef(f.getQcbh(),0)>0){
                    //去重字段
                    qcList.add(f);
                }
            }
            if(qcList.size()>0){
                //对去重字段按去重编号排序
                qcList.sort(Comparator.comparingInt(SysSjglSjzd::getQcbh));
            }else if(!isBlank(getSjdx().getQczd())){
                //有去重字段，此场景为只有一个非主键字段去重
                if(!fields.containsKey(getSjdx().getZddmQczd())){
                    throw new MyException("去重字段配置错误，该字段不存在："+getSjdx().getZddmQczd());
                }
                qcList.add(fields.get(getSjdx().getZddmQczd()));
            }else if(!isBlank(getSjdx().getZjzd())){
                if(!fields.containsKey(getSjdx().getZddmZjzd())){
                    throw new MyException("主键字段配置错误，该字段不存在："+getSjdx().getZddmZjzd());
                }
                //当没有配置去重字段时，直接采用主键作为去重字段
                qcList.add(fields.get(getSjdx().getZddmZjzd()));
            }
            //设置缓存
            fieldsCache.put(cacheKey, fields);
            fieldsCache.put(getSjdx().getId()+ QCZD_LIST, Db.listToMap(qcList,"zddm"));
        }
        fields.forEach((zddm,field)->{
            //将整个字段的验证规则设置到系统验证规则中，因为内部会对规则数据进行修改
            JSONObject gz = field.getKzxxObj().getJSONObject("yzgz");
            if(gz != null){
                myParams.set("$.yzgz['yobj." + zddm + "']",JsonUtil.mergeJSONObjects(
                        myParams.getJSONObject("$.yzgz['yobj." + zddm + "']"),gz));
            }
            gz = field.getKzxxObj().getJSONObject("zhgz");
            if(gz != null){
                myParams.set("$.zhgz['yobj." + zddm + "']",JsonUtil.mergeJSONObjects(
                        myParams.getJSONObject("$.zhgz['yobj." + zddm + "']"),gz));
            }
        });
        myParams.put(KEY_FIELDS, fields);
        myParams.put(KEY_QCFIELDS,fieldsCache.get(getSjdx().getId()+ QCZD_LIST));
    }

    /**
     * 根据字段配置进行验证规则、转换规则、处理类型等初始化 <br/>
     * @param fields   字段列表
     * @param myParams 相关参数
     * @author jingma
     */
    protected void fieldsInit(Map<String, SysSjglSjzd> fields, MyParams myParams) {
        for (SysSjglSjzd field : fields.values()) {
            JSONObject kzxx = field.getKzxxObj();
            //初始化验证规则
            fieldYzgzInit(field, kzxx,myParams);
            //初始化转换规则
            fieldZhgzInit(field, kzxx,myParams);
            //处理类型扩展
            fieldCllxInit(field, kzxx);
        }
    }

    /**
     * 字段处理类型初始化，设置各处理类型是否展示、默认值等
     * @param field 字段
     * @param kzxx 扩展信息
     */
    protected void fieldCllxInit(SysSjglSjzd field, JSONObject kzxx) {
        JSONObject cllxkz = kzxx.getJSONObject("cllxkz");
        if(cllxkz==null){
            cllxkz = new JSONObject();
            kzxx.put("cllxkz",cllxkz);
        }

        //insert：新增页面
        JSONObject lxkz = getKjkzByCllx(field);
        lxkz.put("show",valByDef(TypeUtils.castToBoolean(field.getXzzs()),false));
        lxkz.put("readonly",false);
        lxkz.put("default",field.getXzmrz());
        mergeConfigByCllx(cllxkz, lxkz, Cllx.insert.name());

        //update：编辑
        lxkz = getKjkzByCllx(field);
        lxkz.put("show",valByDef(TypeUtils.castToBoolean(field.getBjzs()),false));
        lxkz.put("readonly",false);
        mergeConfigByCllx(cllxkz, lxkz, Cllx.update.name());

        //dxjcxx：对象基础信息，对应详情页面
        lxkz = getKjkzByCllx(field);
        lxkz.put("show",valByDef(TypeUtils.castToBoolean(field.getXqzs()),false));
        mergeConfigByCllx(cllxkz, lxkz, Cllx.dxjcxx.name());

        //导出字段配置，默认与详情字段一致
        lxkz = getKjkzByCllx(field);
        lxkz.put("show",valByDef(TypeUtils.castToBoolean(field.getXqzs()),false));
        mergeConfigByCllx(cllxkz, lxkz, Cllx.dcsj.name());

        //dcmb：导出模板
        lxkz = getKjkzByCllx(field);
        lxkz.put("show",valByDef(TypeUtils.castToBoolean(field.getMbzs()),false));
        mergeConfigByCllx(cllxkz, lxkz, Cllx.dcmb.name());

        //select：查询页面对应的表单
        lxkz = getKjkzByCllx(field);
        lxkz.put("folding",!valByDef(TypeUtils.castToBoolean(field.getLbzs()),false));
        lxkz.put("readonly",false);
        mergeConfigByCllx(cllxkz, lxkz, Cllx.select.name());

        //other

    }

    /**
     * 根据处理类型配置控件扩展
     * @param field 字段信息
     * @return 通用扩展
     */
    protected JSONObject getKjkzByCllx(SysSjglSjzd field) {
        JSONObject lxkz = new JSONObject();
        lxkz.put("zdkd",field.getZdkd());
        //默认不禁用
        lxkz.put("disabled",!valByDef(TypeUtils.castToBoolean(field.getYxbj()),true));
        //默认只读
        lxkz.put("readonly",true);
        //默认都不展示，只有查询列表控件主动设置为展示
        lxkz.put("show",false);
        return lxkz;
    }

    /**
     * 合并配置-基于处理类型
     * @param pzjh 配置集合
     * @param lxkz 处理类型扩展
     * @param cllx 处理类型
     */
    protected void mergeConfigByCllx(JSONObject pzjh, JSONObject lxkz, String cllx) {
        if (pzjh.containsKey(cllx)) {
            //用户配置了，用户配置的与系统默认的进行合并，用户配置的优先
            JsonUtil.mergeJSONObjects(lxkz, pzjh.getJSONObject(cllx));
        }
        pzjh.put(cllx, lxkz);
    }

    /**
     * 字段转换规则初始化，转换的实现前端就不实现了，后台统一转换
     * @param field 字段
     * @param kzxx 扩展信息
     */
    protected void fieldZhgzInit(SysSjglSjzd field, JSONObject kzxx, MyParams myParams) {
        //默认转换规则
        JSONObject zhgz = new JSONObject();
        //清空前后空格
        zhgz.put("qdqhkg",new JSONObject());
        if("CLOB".equals(field.getZdlx())){
            zhgz.put("clob",new JSONObject());
        }else if("BLOB".equals(field.getZdlx())){
            zhgz.put("blob",new JSONObject());
        }

        JSONObject kzhgz = kzxx.getJSONObject(KEY_ZHGZ);
        if(kzhgz == null){
            kzhgz = new JSONObject();
        }
        mergeConfigByCllx(kzhgz,zhgz,Cllx.select.name());
        zhgz = JsonUtil.clone(zhgz);
        mergeConfigByCllx(kzhgz,zhgz,Cllx.update.name());
        //开始处理新增规则
        zhgz = new JSONObject();
        //设置默认继承更新的规则
        zhgz.put("extends", new String[]{Cllx.update.name()});
        //设置最新地新增验证验证规则
        mergeConfigByCllx(kzhgz,zhgz,Cllx.insert.name());
        //处理验证规则中的继承，后续验证更方便
        for(String cllx:kzhgz.keySet()){
            mergeRule(myParams,kzhgz,cllx);
        }
        kzxx.put(KEY_ZHGZ,kzhgz);
    }
    /**
     * 字段验证规则初始化
     * @param field 字段
     * @param kzxx 扩展信息
     * @param myParams 相关参数
     */
    protected void fieldYzgzInit(SysSjglSjzd field, JSONObject kzxx, MyParams myParams) {
        //默认验证规则
        JSONObject yzgz = new JSONObject(true);
        //查询验证规则
        JSONObject selectYzgz = new JSONObject(true);
        JSONObject gz;
        //必填规则
        if (valByDef(TypeUtils.castToBoolean(field.getBjbt()),false)) {
            yzgz.put("notBlank", new JSONObject());
        }
        //设置信息描述，作为提示的主体
        yzgz.put("xxms", field.getZdmc());
        //设置信息描述，作为提示的主体
        selectYzgz.put("xxms", field.getZdmc());

        //根据控件类型生成规则
        ZdKjlx kjlx = ZdKjlx.getByCode(field.getKjlx());
        if(kjlx!=null){
            switch (kjlx){
                case Switch:
                    field.setZdzdlb(Zdlb.SYS_COMMON_LJPD.name());
                case Select:
                case Radio:
                case Checkbox:
                case ElCascader:
                    gz = new JSONObject();
                    gz.put(VerifyRule.VALUE, field.getZdzdlb());
                    boolean zszdx = !valByDef(TypeUtils.castToBoolean(field.getZdfy()),false);
                    //字典分页，说明字典量较大，不直接展示字典项
                    gz.put("zszdx", zszdx);
                    boolean zddx = valByDef(TypeUtils.castToBoolean(field.getZddx()),false);
                    if(ZdKjlx.Checkbox.equals(kjlx)){
                        zddx = true;
                    }
                    //字典多选
                    gz.put("zddx", zddx);
                    yzgz.put("zd", gz);
                    break;
                case ElInputNumber:
                    yzgz.put("number", new JSONObject());
                    break;
                case ElDatePicker:
                    gz = new JSONObject();
                    //设置默认值，特殊情况可在字段扩展信息中自主扩展
                    gz.put(VerifyRule.VALUE, DateUtil.DATE_FORMATTER14);
                    yzgz.put("date", gz);
                    gz = new JSONObject();
                    selectYzgz.put("dateRange", gz);
                    break;
            }
        }

        //根据字段业务类型设置规则
        if (isBlank(field.getZdywlb())) {
            //为空则忽略
        } else if (field.getZdywlb().contains("sfzh")) {
            yzgz.put("sfzh", new JSONObject());
        } else if (field.getZdywlb().contains("sjh")) {
            //手机号
            gz = new JSONObject();
            gz.put(VerifyRule.VALUE, "^([0-9]{11}|,)*$");
            gz.put(VerifyRule.TS, "只能填写11位手机号，多个电话用英文逗号");
            yzgz.put("zzbds", gz);
        } else if (field.getZdywlb().contains("yx")) {
            //邮箱
            yzgz.put("email", new JSONObject());
        }

        //根据字段类型设置规则
        if ("NUMBER".equals(field.getZdlx())) {
            yzgz.put("number", new JSONObject());
        }
        //根据字段库中的长度设置长度规则。
        if(valByDef(field.getZdcd(),0l)>0){
            gz = new JSONObject();
            gz.put("max", field.getZdcd());
            gz.put("min", 0);
            yzgz.put("length", gz);
        }

        JSONObject kyzgz = kzxx.getJSONObject(KEY_YZGZ);
        if(kyzgz == null){
            kyzgz = new JSONObject();
        }
        //验证规则，新增可以通过继承更新验证规则使用该规则
        mergeConfigByCllx(kyzgz, yzgz, Cllx.update.name());

        //开始处理新增验证规则
        yzgz = new JSONObject(true);
        //必填规则
        if (valByDef(TypeUtils.castToBoolean(field.getBjbt()),false)) {
            yzgz.put("notNull", new JSONObject());
        }
        //设置默认继承更新的验证规则
        yzgz.put("extends", new String[]{Cllx.update.name()});
        mergeConfigByCllx(kyzgz, yzgz, Cllx.insert.name());

        if(valByDef(TypeUtils.castToBoolean(field.getCxbt()),false)){
            //处理查询必填
            selectYzgz.put("notNull",new JSONObject());
        }
        mergeConfigByCllx(kyzgz, selectYzgz, Cllx.select.name());
        //处理验证规则中的继承，后续验证更方便
        for(String cllx:kyzgz.keySet()){
            mergeRule(myParams,kyzgz,cllx);
        }
        kzxx.put(KEY_YZGZ,kyzgz);
    }
    /**
     * 合并验证规则
     * @param myParams 系统参数
     * @param rootRule 所有规则
     * @param cllx 处理类型
     */
    protected void mergeRule(MyParams myParams, JSONObject rootRule, String cllx){
        JSONObject yzgz = rootRule.getJSONObject(cllx);
        if(yzgz==null){
            //该处理类型没有设置验证规则
            return;
        }
        Object et = yzgz.remove("extends");
        if (et != null) {
            //合并继承的规则
            JSONObject newObj = new JSONObject(true);
            for (String eCllx : (String[]) et) {
                JsonUtil.mergeJSONObject(newObj, rootRule.getJSONObject(eCllx));
            }
            JsonUtil.mergeJSONObject(newObj,yzgz);
            rootRule.put(cllx,newObj);
        }
    }

    /**
     * 初始化操作日志记录 <br/>
     * @param myParams 参数
     * @param user     用户
     * @author jingma
     */
    protected void initCzrz(MyParams myParams, SysQxYhxx user) {
        if (valByDef(myParams.sys().getNbdy(),false)) {
            //为系统内部调用
            return;
        }
        SysLogFwzr czrz = new SysLogFwzr();
        myParams.put(KEY_CZRZ, czrz);
        czrz.setId(myParams.sys().getQqid());
        czrz.setCjsj(DateUtil.getGabDate());
        //如果可能被代理的客户端ip与真实ip一致，则只记录一个ip，否则记录两个
        czrz.setCzip(myParams.sys().getClientIp().equals(myParams.sys().getClientRealIp())
                ?myParams.sys().getClientIp():myParams.sys().getClientIp()
                +","+myParams.sys().getClientRealIp());
        czrz.setUrl(myParams.sys().getAuthCode());
        czrz.setToken(user.getToken());
        czrz.setCzlx(myParams.sys().getCllx());
        czrz.setDdjd(Conf.getAppdm());
        if(!isBlank(czrz.getUrl())){
            String[] urlArr = czrz.getUrl().split("_");
            StringBuilder qxm = new StringBuilder(urlArr[0]);
            StringBuilder qxlj = new StringBuilder();
            JSONObject zdobj;
            zdobj = DictManager.zdObjByDm("SYS_QX_QXXX_DM",qxm.toString());
            if(zdobj!=null){
                qxlj.append(zdobj.getString("mc")).append("》");
            }
            for (int i = 1; i<urlArr.length; i++){
                qxm.append("_").append(urlArr[i]);
                zdobj = DictManager.zdObjByDm("SYS_QX_QXXX_DM",qxm.toString());
                if(zdobj!=null){
                    qxlj.append(zdobj.getString("mc")).append("》");
                }
            }
            if(qxlj.toString().endsWith("》")){
                czrz.setCdlj(qxlj.substring(0,qxlj.toString().length()-"》".length()));
                myParams.set("$.sys.cdlj",czrz.getCdlj());
            }else {
                czrz.setCdlj(myParams.sys().getAuthCode());
            }
            zdobj = DictManager.zdObjByDm("SYS_QX_QXXX_DM",
                    myParams.sys().getAuthCode()+"_"+myParams.sys().getCllx());
            czrz.setCzlxmc(zdobj != null?zdobj.getString("mc"):myParams.sys().getCllx());
        }
        //要进行修改，这里进行一个克隆操作
        JSONObject yobj = myParams.getJSONObject(KEY_YOBJ);
        yobj = yobj==null?new JSONObject():yobj.clone();
        if(isBlank(myParams.get(KEY_SJDX))){
            //定制场景
            czrz.setXgcs(StringUtil.substrByByte(yobj,4000));
        }else {
            Map<String, SysSjglSjzd> xtqtzd = getXtqtzd();
            czrz.setSjdx(getSjdx().getId());
            if (StringUtil.isNotBlank(getSjdx().getZjzd()) && StringUtil.isNotBlank(yobj.getString(getSjdx().getZddmZjzd()))) {
                //设置了主键
                czrz.setSjjl(yobj.remove(getSjdx().getZddmZjzd()).toString());
            }
            JSONObject csObj = new JSONObject();
            Map<String, SysSjglSjzd> fields = (Map<String, SysSjglSjzd>) myParams.get(KEY_FIELDS);
            if(fields==null){
                fields = new HashMap<>();
            }
            csObj.put("表单参数",cscl(yobj, fields, xtqtzd));
            int length = csObj.toString().getBytes(StandardCharsets.UTF_8).length;
            //原始输入
            MyParams yssr = JsonUtil.clone(myParams.other().getYsParams());
            yssr.sys().setUserInfo(null);
            //移除单独显示的信息
            Arrays.asList(KEY_YOBJ,KEY_SJDX,$_SYS_IDS,$_SYS_EDITTABLEDATA,$_SYS_TOKEN,
                    $_SYS_AUTHCODE,$_SYS_CLIENT_IP,$_SYS_CLLX).forEach(yssr::remove);
            csObj.put("系统参数",yssr);
            if(csObj.toString().getBytes(StandardCharsets.UTF_8).length>4000){
                csObj.put("系统参数",StringUtil.substrByByte(yssr,3950-length)
                        .replace("\\","").replace("\"","'"));
            }
            length = csObj.toString().getBytes(StandardCharsets.UTF_8).length;
            if(!isBlank(myParams.get($_SYS_IDS))&&length<3950){
                csObj.put("批量操作主键",myParams.get($_SYS_IDS));
                if(csObj.toString().getBytes(StandardCharsets.UTF_8).length>4000){
                    csObj.put("批量操作主键",StringUtil.substrByByte(myParams.getString($_SYS_IDS),3950-length)
                            .replace("\\","").replace("\"","'"));
                }
            }else if(!isBlank(myParams.get($_SYS_EDITTABLEDATA))&&length<3950){
                csObj.put("批量操作表单",myParams.get($_SYS_EDITTABLEDATA));
                if(csObj.toString().getBytes(StandardCharsets.UTF_8).length>4000){
                    csObj.put("批量操作表单",StringUtil.substrByByte(myParams.getString($_SYS_EDITTABLEDATA),3950-length)
                            .replace("\\","").replace("\"","'"));
                }
            }
            czrz.setXgcs(csObj.toString());
        }
    }

    protected JSONObject cscl(JSONObject yobj, Map<String, SysSjglSjzd> fields, Map<String, SysSjglSjzd> xtqtzd) {
        JSONObject csObj = new JSONObject();
        for (Map.Entry<String, Object> e : yobj.entrySet()) {
            if (StringUtil.isBlank(e.getValue())) {
                continue;
            }
            String key = e.getKey();
            String val = yobj.getString(key);
            SysSjglSjzd f = fields.get(key);
            if (f == null) {
                //获取系统其他字段信息
                f = xtqtzd.get(key);
            }
            if (f != null) {
                key = f.getZdmc();
                ZdKjlx kjlx = ZdKjlx.getByCode(f.getKjlx());
                if(kjlx!=null){
                    switch (kjlx){
                        case Switch:
                            f.setZdzdlb(Zdlb.SYS_COMMON_LJPD.name());
                        case Radio:
                        case Checkbox:
                        case Select:
                        case ElCascader:
                            try {
                                val = DictManager.zdMcByMoreDm(f.getZdzdlb(), val);
                            }catch (Exception e1){
                                throw new DictException(e1.getMessage()+"，对应字段："+f);
                            }
                            break;
                        case ElDatePicker:
                            //排除时间范围的场景
                            if(!val.startsWith("[")){
                                val = DateUtil.doFormatDate(DateUtil.parseDate(val), DateUtil.DATE_FORMATTER_L);
                                if (val == null) {
                                    val = e.getValue().toString();
                                }
                            }
                            break;
                        case Password:
                            val = "日志不记录密码";
                            break;

                    }
                }
            }
            if (val.length() > 300) {
                log.trace("操作参数超长：" + key + "->>" + val);
                csObj.put(key, val.substring(0, 300) + "[超长截取]");
            } else {
                csObj.put(key, val);
            }
        }
        return csObj;
    }

    /**
     * 写操作日志
     * @param myParams 相关参数
     * @param r 待返回前端的操作结果
     */
    protected void writeCzrz(MyParams myParams, Result r) {
        SysLogFwzr czrz = myParams.getObject(KEY_CZRZ, SysLogFwzr.class);
        if(czrz!=null){
            czrz.setFhjg(r.isStatus()?Ljpd.TURE.getCode():Ljpd.FALSE.getCode());
            czrz.setFhdm(r.getCode()+"");
            czrz.setFhxx(StringUtil.substrByByte(r.getMsg(),4000));
            czrz.setFhnr(StringUtil.substrByByte(r.getData(),4000).replace("\\t","")
                    .replace("\\n","").replace(" ",""));
            //设置请求耗时
            long kssj = myParams.sys().getQqkssj();
            czrz.setQqhs(System.currentTimeMillis()-kssj);
            SysQxYhxx user = myParams.user();
            db(DEFAULT).lambdaQuery(SysLogFwzr.class,user).insertSelective(czrz);
            czrz.setId(null);
            assert user != null;
            SysPtglXtxx xtxx = SysPtglXtxx.builder().xxnr(czrz.toString())
                    .mdddl("SYS_QX_YHXX").mddxl(user.getId()).build();
            XtxxWebSocket.sendMsg(xtxx,user);
            if(!r.isStatus()){
                xtxx = SysPtglXtxx.builder().xxnr(czrz.toString())
                        .mdddl("SYS_LOG_HTRZ").build();
                XtxxWebSocket.sendMsg(xtxx,user);
            }
        }
    }

    /**
     * 获取系统其他字段 <br/>
     * @return 系统其他字段
     * @author jingma
     */
    protected Map<String, SysSjglSjzd> getXtqtzd() {
        JSONObject xtqtzdParams = LjqManager.jcxxByDxdm("SYS_COMMON_XTQTZD");
        return (Map<String, SysSjglSjzd>) xtqtzdParams.get(KEY_FIELDS);
    }
    /**
     * 生成Excel结果文件
     * @param myParams 相关参数
     * @param header   表头
     * @param data     数据
     * @param fileName 文件名
     * @return 输出为excle的结果
     */
    protected Result resultExcelFile(MyParams myParams, List<List<String>> header, List<List<Object>> data, String fileName) {
        JSONObject sjdxkz = getSjdx().tailsToJSONObject();
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] bytes = new byte[0];
        AbstractColumnWidthStyleStrategy lmcw = new MyLongestMatchColumnWidthStyleStrategy();
        //获取文件类型
        ExcelTypeEnum ete = null;
        SysSjglFile file1 = new SysSjglFile();
        if (myParams.sys().getDcwjm() != null) {
            fileName = myParams.sys().getDcwjm();
        }
//        myParams.set("$.sys.fileType","txt");
        try {
            ete = ExcelTypeEnum.valueOf(StringUtil.fastNotNull(myParams.sys().getFileType(),
                    ExcelTypeEnum.XLSX.name()).toUpperCase());
        }catch (IllegalArgumentException e){
            //easyExcel不能处理
            if ("txt".equals(myParams.sys().getFileType())) {//文本场景
                if (!fileName.contains(".")) {
                    fileName += ".txt";
                }
                file1.setWjlx("txt");
                //分隔符
                String fgf = valByDef(sjdxkz.getString("fgf"), ",");
                //文本限定符
                String wbxdf = valByDef(sjdxkz.getString("wbxdf"),"\"");
                //编码方式
                String bmfs = valByDef(sjdxkz.getString("bmfs"), "utf8");
                StringBuilder context = new StringBuilder();
                for (List<String> h : header) {
                    context.append(fgf).append(wbxdf).append(h.get(0)).append(wbxdf);
                }
                context.append("\n");
                for (List<Object> h : data) {
                    context.append(wbxdf).append(StringUtil.join(h, wbxdf + fgf + wbxdf)).append(wbxdf).append("\n");
                }
                try {
                    bytes = context.substring(fgf.length()).getBytes(bmfs);
                } catch (UnsupportedEncodingException ex) {
                    throw new MyException("生成文本文件时，编码转换失败：" + ex.getMessage(), ex);
                }
            } else {
                throw new MyException("暂不支持的文件类型" + myParams.sys().getFileType());
            }
        }
        if(ete != null){
            if(!fileName.contains(".")){
                fileName += ete.getValue();
            }
            file1.setWjlx(ete.name().toLowerCase());
            List<String> zdlist = myParams.other().getDcsjycl().getZdlist();
            Map<String,CellStyle> cssMap = new HashMap<>();
            final CellStyle[] cssStr = new CellStyle[1];
            Map<String, SysSjglSjzd> fields = myParams.fields();
            try {
                ExcelWriterSheetBuilder ee = EasyExcel.write(os).head(header).autoTrim(true).excelType(ete)
                        //自动列宽，不合适可以自己重写
                        .registerWriteHandler(lmcw).sheet("Sheet1")
                        .registerWriteHandler(new SheetWriteHandler() {

                            public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
                                                         WriteSheetHolder writeSheetHolder) {
                                cssStr[0] = writeWorkbookHolder.getWorkbook().createCellStyle();
                                DataFormat format = writeWorkbookHolder.getWorkbook().createDataFormat();
                                //使导出的Excel格式默认为字符串
                                cssStr[0].setDataFormat(format.getFormat("@"));
                                //自动换行
//                                cssStr[0].setWrapText(true);
                                for (int i = 0; i < header.size(); i++) {
                                    writeSheetHolder.getSheet().setDefaultColumnStyle(i, cssStr[0]);
                                }
                                // 区间设置 第一列第一行和第二行的数据。由于第一行是头，所以第一、二行的数据实际上是第二三行
                                //此处需要循环context.writeContext.writeWorkbookHolder.head进行筛选是否新增下拉框
                                //* firstRow 开始行号(下标0开始)
                                //* lastRow  结束行号，最大65535
                                //* firstCol 区域中第一个单元格的列号 (下标0开始)
                                //* lastCol 区域中最后一个单元格的列号
                                CellRangeAddressList cellRangeAddressList;
                                DataValidationConstraint constraint;
                                DataValidation dataValidation;
                                DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
                                for (int i = 0; i < zdlist.size(); i++) {
                                    String zddm = zdlist.get(i);
                                    SysSjglSjzd f = fields.get(zddm);
                                    if (!isBlank(f.getExcelZdlb())) {
                                        cellRangeAddressList = new CellRangeAddressList(1, 65535, i, i);
                                        constraint = helper.createExplicitListConstraint(f.getExcelZdlb().toArray(new String[0]));
                                        dataValidation = helper.createValidation(constraint, cellRangeAddressList);
                                        writeSheetHolder.getSheet().addValidationData(dataValidation);
                                    }
                                }
                            }


                            public void beforeSheetCreate(SheetWriteHandlerContext context) {
                                context.getWriteSheetHolder().getConverterMap().put(ConverterKeyBuild.buildKey(
                                        ImageConverterKey.class), new ImageConverter());
                            }
                        }).registerWriteHandler(new CellWriteHandler() {

                            public void afterCellDispose(CellWriteHandlerContext context) {
                                String zddm = zdlist.get(context.getColumnIndex());
                                SysSjglSjzd f = fields.get(zddm);
                                if (context.getHead() && f.getDcbtbjs() != null) {
                                    //是表头且设置了表头颜色
                                    WriteCellStyle css = context.getFirstCellData().getOrCreateStyle();
                                    css.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
                                    css.setFillForegroundColor(IndexedColors.valueOf(f.getDcbtbjs()).getIndex());
                                } else {
//                        String ys = IndexedColors.WHITE.name();
                                    String ys = null;
                                    if (f.getDcmrbjs() != null) {
                                        ys = f.getDcmrbjs();
                                    }
                                    if (f.getDcdzbjs().get(context.getRowIndex().toString()) != null) {
                                        ys = f.getDcdzbjs().get(context.getRowIndex().toString()).toString();
                                    }
                                    //要设置颜色
                                    if (ys != null) {
                                        //该对象缓存起来，创建超过6w会报错
                                        CellStyle css = cssMap.get(ys);
                                        if (css == null) {
                                            css = context.getWriteWorkbookHolder().getWorkbook().createCellStyle();
                                            css.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                                            //设置为字符串格式
                                            DataFormat format = context.getWriteWorkbookHolder().getWorkbook().createDataFormat();
                                            css.setDataFormat(format.getFormat("@"));
                                            css.setFillForegroundColor(IndexedColors.valueOf(ys).getIndex());
                                            cssMap.put(ys, css);
                                        }
                                        context.getCell().setCellStyle(css);
                                    } else {
                                        //设置为字符串格式
                                        context.getCell().setCellStyle(cssStr[0]);
                                    }
                                }
                            }
                        });
                if(myParams.other().getDcsjycl().getZjhg()){
                    //增加行高
                    ee.registerWriteHandler(new SimpleRowHeightStyleStrategy(null, (short) 100));
                }
                ee.doWrite(data);
            }catch (Throwable t){
                throw new MyException("生成Excel异常，header："+JSON.toJSONString(header)+"，data："+JSON.toJSONString(data),t);
            }
            if(ete.equals(ExcelTypeEnum.CSV)){
                try {
                    //csv格式转为GBK编码
                    bytes = os.toString().getBytes("GBK");
                }catch (Exception e){
                    throw new MyException("csv文件编码转换失败："+e.getMessage(),e);
                }
            }else {
                bytes = os.toByteArray();
            }
        }
        file1.setWjm(fileName);
        return resultFile(bytes, file1);
    }

    /**
     * @param byteArr 字节流
     * @param file    文件描述
     * @return 输出为文件的结果
     */
    protected Result resultFile(byte[] byteArr, SysSjglFile file) {
        file.setBytes(byteArr);
        Result r = success("生成文件结果对象成功", file);
        r.setDateType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        return r;
    }

    /**
     * @see LjqInterface#init()
     */

    public void init() {
        log.debug("拦截器初始化：" + this.getClass().getSimpleName());
    }

    /**
     * 异步结果处理
     */
    private Result ybjgcl(MyParams myParams, Result r,boolean sfyb) {
        if(myParams.get($_SYS_HDCST)==null||sfyb||!r.isStatus()){
            //没有回调，或本身就是异步参数体方式
            return r;
        }
        String hdqqid = r.getQqid();
        //设置请求的回调方法等待回调
        defaultCache.put(hdqqid, myParams.get($_SYS_HDCST));
        //采用锁等待返回结果，超时时长配置
        Lock lock = new ReentrantLock();
        try {
            lock.lock();
            Condition cond = lock.newCondition();
            defaultCache.put("lock_"+hdqqid,lock);
            defaultCache.put("cond_"+hdqqid,cond);
            if (!cond.await(StringUtil.fastNotNull(myParams.sys().getTimeout(),
                    Conf.getUtilConfig().getZnjh().getCtdydd(), 60000000),TimeUnit.MILLISECONDS)) {
                r = failed("等待回调超时");
                r.setCode(XtZtm.CTDYCS.getCode());
            }
            if(!isBlank(defaultCache.get("result_"+hdqqid))){
                //取回调结束时的结果
                r = defaultCache.getObject("result_"+hdqqid,Result.class);
                if(!r.isStatus()){
                    throw r.newMyException();
                }
            }
            return r;
        } catch (InterruptedException e) {
            return failed("等待回调被中断");
        }finally {
            lock.unlock();
            defaultCache.remove(hdqqid);
            defaultCache.remove("lock_"+hdqqid);
            defaultCache.remove("cond_"+hdqqid);
            defaultCache.remove("result_"+hdqqid);
        }
    }

    /**
     * 回调方法转换为回调参数体
     * @return 是否需要单独开启线程进行处理，而直接返回结果，是否异步
     */
    private boolean hdffToHdcst(MyParams ysParams,MyParams myParams) {
        if(myParams.get($_SYS_HDCST)==null){
            //没有回调机制，正常单线程执行
            return false;
        }
        if(myParams.get($_SYS_HDCST) instanceof Map){
            //回调参数体场景，采用开线程异步方式
            return true;
        }
        //存在回调参数体，且回调参数体是回调方法，此场景不开线程
        MyParams hdcst = new MyParams();
        hdcst.sys().setCllx(Cllx.tyhd.name());
        hdcst.sjdx().setDxdm(getSjdx().getDxdm());
        //设置穿透app，默认设置，不需要穿透也没关系
        hdcst.sys().setCtapp(Conf.getAppdm());
        JsonUtil.copy(hdcst,myParams,$_SYS_TIMEOUT);
        ysParams.sys().setHdcst(hdcst);
        return false;
    }

    /**
     * 数据批量处理结果
     */
    protected Result sjplcljg(int count, int insert, int update, int ignore, int repeat) {
        JSONObject re = new JSONObject();
        re.put("count",count);
        re.put("insert",insert);
        re.put("update",update);
        re.put("ignore",ignore);
        re.put("repeat",repeat);
        return success("正常处理共计"+count+"条记录，其中新增"+insert+"条，更新"+update+"条，忽略"+ignore+"条，重复"+repeat+"条",re);
    }

}
